activerecord-multi-tenant 1.2.0 → 2.4.0
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/.github/workflows/active-record-multi-tenant-tests.yml +83 -0
- data/.gitignore +6 -0
- data/.readthedocs.yaml +15 -0
- data/.rspec +0 -0
- data/.rubocop.yml +51 -0
- data/Appraisals +6 -22
- data/CHANGELOG.md +51 -0
- data/Gemfile +3 -1
- data/LICENSE +0 -0
- data/README.md +3 -2
- data/Rakefile +1 -1
- data/activerecord-multi-tenant.gemspec +28 -23
- data/docker-compose.yml +24 -18
- data/docs/.gitignore +3 -0
- data/docs/Makefile +28 -0
- data/docs/api-reference.sh +10 -0
- data/docs/requirements.in +4 -0
- data/docs/requirements.txt +62 -0
- data/docs/source/_static/api-reference/ActiveRecord/Associations/Association.html +285 -0
- data/docs/source/_static/api-reference/ActiveRecord/Associations/ClassMethods.html +255 -0
- data/docs/source/_static/api-reference/ActiveRecord/Associations.html +117 -0
- data/docs/source/_static/api-reference/ActiveRecord/ConnectionAdapters/SchemaStatements.html +232 -0
- data/docs/source/_static/api-reference/ActiveRecord/ConnectionAdapters.html +126 -0
- data/docs/source/_static/api-reference/ActiveRecord/QueryMethods.html +336 -0
- data/docs/source/_static/api-reference/ActiveRecord/SchemaDumper.html +121 -0
- data/docs/source/_static/api-reference/ActiveRecord.html +130 -0
- data/docs/source/_static/api-reference/MultiTenant/ArelTenantVisitor.html +755 -0
- data/docs/source/_static/api-reference/MultiTenant/ArelVisitorsDepthFirst.html +208 -0
- data/docs/source/_static/api-reference/MultiTenant/BaseTenantEnforcementClause.html +462 -0
- data/docs/source/_static/api-reference/MultiTenant/Context.html +659 -0
- data/docs/source/_static/api-reference/MultiTenant/ControllerExtensions.html +202 -0
- data/docs/source/_static/api-reference/MultiTenant/CopyFromClient.html +186 -0
- data/docs/source/_static/api-reference/MultiTenant/CopyFromClientHelper.html +362 -0
- data/docs/source/_static/api-reference/MultiTenant/Current.html +124 -0
- data/docs/source/_static/api-reference/MultiTenant/DatabaseStatements.html +366 -0
- data/docs/source/_static/api-reference/MultiTenant/FastTruncate.html +226 -0
- data/docs/source/_static/api-reference/MultiTenant/MigrationExtensions.html +554 -0
- data/docs/source/_static/api-reference/MultiTenant/MissingTenantError.html +124 -0
- data/docs/source/_static/api-reference/MultiTenant/ModelExtensionsClassMethods.html +492 -0
- data/docs/source/_static/api-reference/MultiTenant/QueryMonitor.html +257 -0
- data/docs/source/_static/api-reference/MultiTenant/Table.html +419 -0
- data/docs/source/_static/api-reference/MultiTenant/TenantEnforcementClause.html +148 -0
- data/docs/source/_static/api-reference/MultiTenant/TenantIsImmutable.html +135 -0
- data/docs/source/_static/api-reference/MultiTenant/TenantJoinEnforcementClause.html +310 -0
- data/docs/source/_static/api-reference/MultiTenant/TenantValueVisitor.html +239 -0
- data/docs/source/_static/api-reference/MultiTenant.html +1454 -0
- data/docs/source/_static/api-reference/MultiTenantFindBy.html +180 -0
- data/docs/source/_static/api-reference/Sidekiq/Client.html +302 -0
- data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant/Client.html +217 -0
- data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant/Server.html +219 -0
- data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant.html +126 -0
- data/docs/source/_static/api-reference/Sidekiq.html +126 -0
- data/docs/source/_static/api-reference/_index.html +399 -0
- data/docs/source/_static/api-reference/class_list.html +51 -0
- data/docs/source/_static/api-reference/css/common.css +1 -0
- data/docs/source/_static/api-reference/css/full_list.css +58 -0
- data/docs/source/_static/api-reference/css/style.css +497 -0
- data/docs/source/_static/api-reference/file.README.html +167 -0
- data/docs/source/_static/api-reference/file_list.html +56 -0
- data/docs/source/_static/api-reference/frames.html +17 -0
- data/docs/source/_static/api-reference/index.html +167 -0
- data/docs/source/_static/api-reference/js/app.js +314 -0
- data/docs/source/_static/api-reference/js/full_list.js +216 -0
- data/docs/source/_static/api-reference/js/jquery.js +4 -0
- data/docs/source/_static/api-reference/method_list.html +715 -0
- data/docs/source/_static/api-reference/top-level-namespace.html +126 -0
- data/docs/source/_templates/.gitignore +4 -0
- data/docs/source/api-reference.rst +8 -0
- data/docs/source/appendix.rst +26 -0
- data/docs/source/changelog.rst +8 -0
- data/docs/source/community-and-support.rst +26 -0
- data/docs/source/conf.py +30 -0
- data/docs/source/contributing.rst +70 -0
- data/docs/source/getting-started.rst +37 -0
- data/docs/source/guides-and-tutorials.rst +129 -0
- data/docs/source/index.rst +54 -0
- data/docs/source/introduction.rst +33 -0
- data/docs/source/license.rst +22 -0
- data/docs/source/troubleshooting.rst +41 -0
- data/docs/source/usage-guide.rst +59 -0
- data/lib/activerecord-multi-tenant/arel_visitors_depth_first.rb +183 -174
- data/lib/activerecord-multi-tenant/controller_extensions.rb +15 -4
- data/lib/activerecord-multi-tenant/copy_from_client.rb +4 -0
- data/lib/activerecord-multi-tenant/fast_truncate.rb +4 -2
- data/lib/activerecord-multi-tenant/habtm.rb +50 -0
- data/lib/activerecord-multi-tenant/migrations.rb +87 -10
- data/lib/activerecord-multi-tenant/model_extensions.rb +98 -34
- data/lib/activerecord-multi-tenant/multi_tenant.rb +102 -29
- data/lib/activerecord-multi-tenant/query_monitor.rb +21 -5
- data/lib/activerecord-multi-tenant/query_rewriter.rb +122 -91
- data/lib/activerecord-multi-tenant/sidekiq.rb +46 -19
- data/lib/activerecord-multi-tenant/table_node.rb +13 -0
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/lib/activerecord-multi-tenant.rb +3 -13
- data/lib/activerecord_multi_tenant.rb +13 -0
- data/spec/activerecord-multi-tenant/associations_spec.rb +42 -0
- data/spec/activerecord-multi-tenant/controller_extensions_spec.rb +3 -2
- data/spec/activerecord-multi-tenant/fast_truncate_spec.rb +8 -6
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +347 -143
- data/spec/activerecord-multi-tenant/multi_tenant_spec.rb +69 -13
- data/spec/activerecord-multi-tenant/query_rewriter_spec.rb +60 -59
- data/spec/activerecord-multi-tenant/record_callback_spec.rb +0 -0
- data/spec/activerecord-multi-tenant/record_finding_spec.rb +11 -11
- data/spec/activerecord-multi-tenant/record_modifications_spec.rb +23 -4
- data/spec/activerecord-multi-tenant/sidekiq_spec.rb +10 -10
- data/spec/database.yml +0 -0
- data/spec/schema.rb +43 -2
- data/spec/spec_helper.rb +52 -16
- data/spec/support/format_sql.rb +20 -0
- metadata +126 -36
- data/.github/workflows/CI.yml +0 -63
- data/gemfiles/.bundle/config +0 -2
- data/gemfiles/active_record_5.2.gemfile +0 -16
- data/gemfiles/active_record_6.0.gemfile +0 -8
- data/gemfiles/active_record_6.1.gemfile +0 -8
- data/gemfiles/active_record_7.0.gemfile +0 -8
- data/gemfiles/rails_5.2.gemfile +0 -16
- data/gemfiles/rails_6.0.gemfile +0 -8
- data/gemfiles/rails_6.1.gemfile +0 -8
- data/gemfiles/rails_7.0.gemfile +0 -8
- data/lib/activerecord-multi-tenant/persistence_extension.rb +0 -13
- data/lib/activerecord-multi-tenant/with_lock.rb +0 -15
- data/spec/activerecord-multi-tenant/schema_dumper_tester.rb +0 -0
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
RSpec.describe MultiTenant do
|
4
|
-
describe
|
6
|
+
describe '.load_current_tenant!' do
|
5
7
|
let(:fake_tenant) { OpenStruct.new(id: 1) }
|
6
8
|
let(:mock_klass) { double(find: fake_tenant) }
|
7
9
|
|
@@ -14,14 +16,14 @@ RSpec.describe MultiTenant do
|
|
14
16
|
MultiTenant.default_tenant_class = @original_default_class
|
15
17
|
end
|
16
18
|
|
17
|
-
it
|
19
|
+
it 'sets and returns the loaded current_tenant' do
|
18
20
|
expect(mock_klass).to receive(:find).once.with(1)
|
19
21
|
MultiTenant.current_tenant = 1
|
20
22
|
expect(MultiTenant.load_current_tenant!).to eq(fake_tenant)
|
21
23
|
expect(MultiTenant.current_tenant).to eq(fake_tenant)
|
22
24
|
end
|
23
25
|
|
24
|
-
it
|
26
|
+
it 'respects `.with` lifecycle' do
|
25
27
|
expect(mock_klass).to receive(:find).once.with(2)
|
26
28
|
expect(MultiTenant.current_tenant).to eq(nil)
|
27
29
|
MultiTenant.with(2) do
|
@@ -31,34 +33,88 @@ RSpec.describe MultiTenant do
|
|
31
33
|
expect(MultiTenant.current_tenant).to eq(nil)
|
32
34
|
end
|
33
35
|
|
34
|
-
context
|
35
|
-
it
|
36
|
+
context 'with a loaded current_tenant' do
|
37
|
+
it 'returns the tenant without fetching it' do
|
36
38
|
expect(mock_klass).not_to receive(:find)
|
37
39
|
MultiTenant.current_tenant = fake_tenant
|
38
40
|
expect(MultiTenant.load_current_tenant!).to eq(fake_tenant)
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
42
|
-
context
|
43
|
-
it
|
44
|
+
context 'with a nil current_tenant' do
|
45
|
+
it 'raises an error, as there is not enough information to load the tenant' do
|
44
46
|
expect(mock_klass).not_to receive(:find)
|
45
|
-
expect
|
47
|
+
expect do
|
46
48
|
MultiTenant.load_current_tenant!
|
47
|
-
|
49
|
+
end.to raise_error(RuntimeError, 'MultiTenant.current_tenant must be set to load')
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
51
|
-
context
|
53
|
+
context 'without a default class set' do
|
52
54
|
before do
|
53
55
|
MultiTenant.default_tenant_class = nil
|
54
56
|
end
|
55
57
|
|
56
|
-
it
|
58
|
+
it 'raises an error, as there is not enough information to load the tenant' do
|
57
59
|
expect(mock_klass).not_to receive(:find)
|
58
60
|
MultiTenant.current_tenant = 1
|
59
|
-
expect
|
61
|
+
expect do
|
60
62
|
MultiTenant.load_current_tenant!
|
61
|
-
|
63
|
+
end.to raise_error(RuntimeError, 'Only have tenant id, and no default tenant class set')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '.tenant_klass_defined?' do
|
69
|
+
context 'without options' do
|
70
|
+
before(:all) do
|
71
|
+
class SampleTenant < ActiveRecord::Base
|
72
|
+
multi_tenant :sample_tenant
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'return true with valid tenant_name' do
|
77
|
+
expect(MultiTenant.tenant_klass_defined?(:sample_tenant)).to eq(true)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'return false with invalid_tenant_name' do
|
81
|
+
invalid_tenant_name = :tenant
|
82
|
+
expect(MultiTenant.tenant_klass_defined?(invalid_tenant_name)).to eq(false)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with options' do
|
87
|
+
context 'and valid class_name' do
|
88
|
+
it 'return true' do
|
89
|
+
class SampleTenant < ActiveRecord::Base
|
90
|
+
multi_tenant :tenant
|
91
|
+
end
|
92
|
+
|
93
|
+
tenant_name = :tenant
|
94
|
+
options = {
|
95
|
+
class_name: 'SampleTenant'
|
96
|
+
}
|
97
|
+
expect(MultiTenant.tenant_klass_defined?(tenant_name, options)).to eq(true)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'return true when tenant class is nested' do
|
101
|
+
module SampleModule
|
102
|
+
class SampleNestedTenant < ActiveRecord::Base
|
103
|
+
multi_tenant :tenant
|
104
|
+
end
|
105
|
+
# rubocop:disable Layout/TrailingWhitespace
|
106
|
+
# Trailing whitespace is intentionally left here
|
107
|
+
|
108
|
+
class AnotherTenant < ActiveRecord::Base
|
109
|
+
end
|
110
|
+
# rubocop:enable Layout/TrailingWhitespace
|
111
|
+
end
|
112
|
+
tenant_name = :tenant
|
113
|
+
options = {
|
114
|
+
class_name: 'SampleModule::SampleNestedTenant'
|
115
|
+
}
|
116
|
+
expect(MultiTenant.tenant_klass_defined?(tenant_name, options)).to eq(true)
|
117
|
+
end
|
62
118
|
end
|
63
119
|
end
|
64
120
|
end
|
@@ -1,112 +1,113 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'spec_helper'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
let!(:
|
8
|
-
let!(:
|
5
|
+
describe 'Query Rewriter' do
|
6
|
+
context 'when bulk updating' do
|
7
|
+
let!(:account) { Account.create!(name: 'Test Account') }
|
8
|
+
let!(:project) { Project.create(name: 'Project 1', account: account) }
|
9
|
+
let!(:manager) { Manager.create(name: 'Manager', project: project, account: account) }
|
9
10
|
|
10
|
-
it
|
11
|
-
expect
|
11
|
+
it 'updates the records' do
|
12
|
+
expect do
|
12
13
|
MultiTenant.with(account) do
|
13
|
-
Project.joins(:manager).update_all(name:
|
14
|
+
Project.joins(:manager).update_all(name: 'New Name')
|
14
15
|
end
|
15
|
-
|
16
|
+
end.to change { project.reload.name }.from('Project 1').to('New Name')
|
16
17
|
end
|
17
18
|
|
18
|
-
it
|
19
|
-
expect
|
20
|
-
Project.joins(:manager).update_all(name:
|
21
|
-
|
19
|
+
it 'updates the records without a current tenant' do
|
20
|
+
expect do
|
21
|
+
Project.joins(:manager).update_all(name: 'New Name')
|
22
|
+
end.to change { project.reload.name }.from('Project 1').to('New Name')
|
22
23
|
end
|
23
24
|
|
24
|
-
it
|
25
|
-
expect
|
25
|
+
it 'update the record' do
|
26
|
+
expect do
|
26
27
|
MultiTenant.with(account) do
|
27
|
-
project.update(name:
|
28
|
+
project.update(name: 'New Name')
|
28
29
|
end
|
29
|
-
|
30
|
+
end.to change { project.reload.name }.from('Project 1').to('New Name')
|
30
31
|
end
|
31
32
|
|
32
|
-
it
|
33
|
-
expect
|
34
|
-
project.update(name:
|
35
|
-
|
33
|
+
it 'update the record without a current tenant' do
|
34
|
+
expect do
|
35
|
+
project.update(name: 'New Name')
|
36
|
+
end.to change { project.reload.name }.from('Project 1').to('New Name')
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
|
-
context
|
40
|
-
let!(:account) { Account.create!(name:
|
41
|
-
let!(:project1) { Project.create(name:
|
42
|
-
let!(:project2) { Project.create(name:
|
43
|
-
let!(:project3) { Project.create(name:
|
44
|
-
let!(:manager1) { Manager.create(name:
|
45
|
-
let!(:manager2) { Manager.create(name:
|
40
|
+
context 'when bulk deleting' do
|
41
|
+
let!(:account) { Account.create!(name: 'Test Account') }
|
42
|
+
let!(:project1) { Project.create(name: 'Project 1', account: account) }
|
43
|
+
let!(:project2) { Project.create(name: 'Project 2', account: account) }
|
44
|
+
let!(:project3) { Project.create(name: 'Project 3', account: account) }
|
45
|
+
let!(:manager1) { Manager.create(name: 'Manager 1', project: project1, account: account) }
|
46
|
+
let!(:manager2) { Manager.create(name: 'Manager 2', project: project2, account: account) }
|
46
47
|
|
47
|
-
it
|
48
|
-
expect
|
48
|
+
it 'delete_all the records' do
|
49
|
+
expect do
|
49
50
|
MultiTenant.with(account) do
|
50
51
|
Project.joins(:manager).delete_all
|
51
52
|
end
|
52
|
-
|
53
|
+
end.to change { Project.count }.from(3).to(1)
|
53
54
|
end
|
54
55
|
|
55
|
-
it
|
56
|
-
expect
|
56
|
+
it 'delete_all the records without a current tenant' do
|
57
|
+
expect do
|
57
58
|
Project.joins(:manager).delete_all
|
58
|
-
|
59
|
+
end.to change { Project.count }.from(3).to(1)
|
59
60
|
end
|
60
61
|
|
61
|
-
it
|
62
|
-
expect
|
62
|
+
it 'delete the record' do
|
63
|
+
expect do
|
63
64
|
MultiTenant.with(account) do
|
64
65
|
project1.delete
|
65
66
|
Project.delete(project2.id)
|
66
67
|
end
|
67
|
-
|
68
|
+
end.to change { Project.count }.from(3).to(1)
|
68
69
|
end
|
69
70
|
|
70
|
-
it
|
71
|
-
expect
|
71
|
+
it 'delete the record without a current tenant' do
|
72
|
+
expect do
|
72
73
|
project1.delete
|
73
74
|
Project.delete(project2.id)
|
74
|
-
|
75
|
+
end.to change { Project.count }.from(3).to(1)
|
75
76
|
end
|
76
77
|
|
77
|
-
it
|
78
|
-
expect
|
78
|
+
it 'destroy the record' do
|
79
|
+
expect do
|
79
80
|
MultiTenant.with(account) do
|
80
81
|
project1.destroy
|
81
82
|
Project.destroy(project2.id)
|
82
83
|
end
|
83
|
-
|
84
|
+
end.to change { Project.count }.from(3).to(1)
|
84
85
|
end
|
85
86
|
|
86
|
-
it
|
87
|
-
expect
|
87
|
+
it 'destroy the record without a current tenant' do
|
88
|
+
expect do
|
88
89
|
project1.destroy
|
89
90
|
Project.destroy(project2.id)
|
90
|
-
|
91
|
+
end.to change { Project.count }.from(3).to(1)
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
94
|
-
context
|
95
|
-
it
|
96
|
-
expect
|
97
|
-
ActiveRecord::Base.connection.update(
|
98
|
-
|
95
|
+
context 'when update without arel' do
|
96
|
+
it 'can call method' do
|
97
|
+
expect do
|
98
|
+
ActiveRecord::Base.connection.update('SELECT 1')
|
99
|
+
end.not_to raise_error
|
99
100
|
end
|
100
101
|
end
|
101
102
|
|
102
|
-
context
|
103
|
-
let!(:account) { Account.create!(name:
|
103
|
+
context 'when joining with a model with a default scope' do
|
104
|
+
let!(:account) { Account.create!(name: 'Test Account') }
|
104
105
|
|
105
|
-
it
|
106
|
-
alive = Domain.create(name:
|
107
|
-
deleted = Domain.create(name:
|
108
|
-
page_in_alive_domain = Page.create(name:
|
109
|
-
|
106
|
+
it 'fetches only records within the default scope' do
|
107
|
+
alive = Domain.create(name: 'alive', account: account)
|
108
|
+
deleted = Domain.create(name: 'deleted', deleted: true, account: account)
|
109
|
+
page_in_alive_domain = Page.create(name: 'alive', account: account, domain: alive)
|
110
|
+
Page.create(name: 'deleted', account: account, domain: deleted)
|
110
111
|
|
111
112
|
expect(
|
112
113
|
MultiTenant.with(account) do
|
File without changes
|
@@ -61,36 +61,36 @@ describe MultiTenant, 'Record finding' do
|
|
61
61
|
end
|
62
62
|
|
63
63
|
context 'model with has_many relation through multi-tenant model' do
|
64
|
-
let(:
|
65
|
-
let(:
|
64
|
+
let(:tenant1) { Account.create! name: 'Tenant 1' }
|
65
|
+
let(:project1) { tenant1.projects.create! }
|
66
66
|
|
67
|
-
let(:
|
68
|
-
let(:
|
67
|
+
let(:tenant2) { Account.create! name: 'Tenant 2' }
|
68
|
+
let(:project2) { tenant2.projects.create! }
|
69
69
|
|
70
70
|
let(:category) { Category.create! name: 'Category' }
|
71
71
|
|
72
72
|
before do
|
73
|
-
ProjectCategory.create! account:
|
74
|
-
ProjectCategory.create! account:
|
73
|
+
ProjectCategory.create! account: tenant1, name: '1', project: project1, category: category
|
74
|
+
ProjectCategory.create! account: tenant2, name: '2', project: project2, category: category
|
75
75
|
end
|
76
76
|
|
77
77
|
it 'can get model without creating query cache' do
|
78
|
-
MultiTenant.with(
|
79
|
-
found_category = Project.find(
|
78
|
+
MultiTenant.with(tenant1) do
|
79
|
+
found_category = Project.find(project1.id).categories.to_a.first
|
80
80
|
expect(found_category).to eq(category)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
84
|
it 'can get model for other tenant' do
|
85
|
-
MultiTenant.with(
|
86
|
-
found_category = Project.find(
|
85
|
+
MultiTenant.with(tenant2) do
|
86
|
+
found_category = Project.find(project2.id).categories.to_a.first
|
87
87
|
expect(found_category).to eq(category)
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
91
|
it 'can get model without current_tenant' do
|
92
92
|
MultiTenant.without do
|
93
|
-
found_category = Project.find(
|
93
|
+
found_category = Project.find(project2.id).categories.to_a.first
|
94
94
|
expect(found_category).to eq(category)
|
95
95
|
end
|
96
96
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe MultiTenant, 'Record modifications' do
|
@@ -6,7 +8,6 @@ describe MultiTenant, 'Record modifications' do
|
|
6
8
|
let(:project) { Project.create! name: 'something', account: account }
|
7
9
|
let(:project2) { Project.create! name: 'something2', account: account2, id: project.id }
|
8
10
|
|
9
|
-
|
10
11
|
it 'includes the tenant_id in DELETEs when using object.destroy' do
|
11
12
|
# two records with same id but different account_id
|
12
13
|
# when doing project.destroy it should delete only the current one
|
@@ -16,7 +17,7 @@ describe MultiTenant, 'Record modifications' do
|
|
16
17
|
expect(project2.account).to eq(account2)
|
17
18
|
expect(project.id).to eq(project2.id)
|
18
19
|
|
19
|
-
MultiTenant.without
|
20
|
+
MultiTenant.without do
|
20
21
|
expect(Project.count).to eq(2)
|
21
22
|
project.destroy
|
22
23
|
expect(Project.count).to eq(1)
|
@@ -28,7 +29,6 @@ describe MultiTenant, 'Record modifications' do
|
|
28
29
|
MultiTenant.with(account2) do
|
29
30
|
expect(Project.where(id: project2.id).first).to be_present
|
30
31
|
end
|
31
|
-
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'includes the tenant_id in DELETEs when using object.delete' do
|
@@ -40,7 +40,7 @@ describe MultiTenant, 'Record modifications' do
|
|
40
40
|
expect(project2.account).to eq(account2)
|
41
41
|
expect(project.id).to eq(project2.id)
|
42
42
|
|
43
|
-
MultiTenant.without
|
43
|
+
MultiTenant.without do
|
44
44
|
expect(Project.count).to eq(2)
|
45
45
|
project.delete
|
46
46
|
expect(Project.count).to eq(1)
|
@@ -54,6 +54,25 @@ describe MultiTenant, 'Record modifications' do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
it 'should not update other objects with same id when calling object.update_columns' do
|
58
|
+
# When two records with same id but different account_id are updated, it should only update the current one
|
59
|
+
expect(project.account).to eq(account)
|
60
|
+
expect(project2.account).to eq(account2)
|
61
|
+
expect(project.id).to eq(project2.id)
|
62
|
+
|
63
|
+
MultiTenant.without do
|
64
|
+
project2.update_columns(name: 'newthing2')
|
65
|
+
expect(project.reload.name).to eq('something')
|
66
|
+
expect(project2.reload.name).to eq('newthing2')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should return the same object when calling object.reload' do
|
71
|
+
# When two records with same id but different account_id are updated, it should not return the other object
|
72
|
+
expect(project.reload.account_id).to eq(account.id)
|
73
|
+
expect(project2.reload.account_id).to eq(account2.id)
|
74
|
+
end
|
75
|
+
|
57
76
|
it 'test delete for reference tables' do
|
58
77
|
category1 = Category.create! name: 'Category 1'
|
59
78
|
expect(Category.count).to eq(1)
|
@@ -5,29 +5,29 @@ require 'activerecord-multi-tenant/sidekiq'
|
|
5
5
|
describe MultiTenant, 'Sidekiq' do
|
6
6
|
let(:server) { Sidekiq::Middleware::MultiTenant::Server.new }
|
7
7
|
let(:account) { Account.create(name: 'test') }
|
8
|
-
let(:
|
8
|
+
let(:deleted_account) { Account.create(name: 'deleted') }
|
9
9
|
|
10
|
-
before {
|
10
|
+
before { deleted_account.destroy! }
|
11
11
|
|
12
12
|
describe 'server middleware' do
|
13
13
|
it 'sets the multitenant context when provided in message' do
|
14
|
-
server.call(double,{'bogus' => 'message',
|
15
|
-
|
16
|
-
|
14
|
+
server.call(double, { 'bogus' => 'message',
|
15
|
+
'multi_tenant' => { 'class' => account.class.name, 'id' => account.id } },
|
16
|
+
'bogus_queue') do
|
17
17
|
expect(MultiTenant.current_tenant).to eq(account)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'sets the multitenant context (id) even if tenant not found' do
|
22
|
-
server.call(double,{'bogus' => 'message',
|
23
|
-
|
24
|
-
|
25
|
-
expect(MultiTenant.current_tenant).to eq(
|
22
|
+
server.call(double, { 'bogus' => 'message',
|
23
|
+
'multi_tenant' => { 'class' => deleted_account.class.name, 'id' => deleted_account.id } },
|
24
|
+
'bogus_queue') do
|
25
|
+
expect(MultiTenant.current_tenant).to eq(deleted_account.id)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'does not set the multitenant context when no tenant provided' do
|
30
|
-
server.call(double, {'bogus' => 'message'}, 'bogus_queue') do
|
30
|
+
server.call(double, { 'bogus' => 'message' }, 'bogus_queue') do
|
31
31
|
expect(MultiTenant.current_tenant).to be_nil
|
32
32
|
end
|
33
33
|
end
|
data/spec/database.yml
CHANGED
File without changes
|
data/spec/schema.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Resets the database, except when we are only running a specific spec
|
2
4
|
ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
3
5
|
enable_extension_on_all_nodes 'uuid-ossp'
|
@@ -7,6 +9,7 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
7
9
|
t.column :name, :string
|
8
10
|
t.column :subdomain, :string
|
9
11
|
t.column :domain, :string
|
12
|
+
t.column :password, :string
|
10
13
|
end
|
11
14
|
|
12
15
|
create_table :projects, force: true, partition_key: :account_id do |t|
|
@@ -27,6 +30,17 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
27
30
|
t.column :completed, :boolean
|
28
31
|
end
|
29
32
|
|
33
|
+
create_table :managers_tasks, force: true, partition_key: :account_id do |t|
|
34
|
+
t.column :account_id, :integer
|
35
|
+
t.column :manager_id, :integer
|
36
|
+
t.column :task_id, :integer
|
37
|
+
end
|
38
|
+
|
39
|
+
create_table :managers_projects, force: true do |t|
|
40
|
+
t.column :project_id, :integer
|
41
|
+
t.column :manager_id, :integer
|
42
|
+
end
|
43
|
+
|
30
44
|
create_table :sub_tasks, force: true, partition_key: :account_id do |t|
|
31
45
|
t.column :account_id, :integer
|
32
46
|
t.column :name, :string
|
@@ -34,6 +48,13 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
34
48
|
t.column :type, :string
|
35
49
|
end
|
36
50
|
|
51
|
+
create_table :optional_sub_tasks, force: true do |t|
|
52
|
+
t.references :account, :integer
|
53
|
+
t.column :sub_task_id, :integer
|
54
|
+
t.column :name, :string
|
55
|
+
t.column :type, :string
|
56
|
+
end
|
57
|
+
|
37
58
|
create_table :countries, force: true do |t|
|
38
59
|
t.column :name, :string
|
39
60
|
end
|
@@ -106,6 +127,11 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
106
127
|
t.column :domain_id, :integer
|
107
128
|
end
|
108
129
|
|
130
|
+
create_table :posts, force: true, partition_key: :account_id do |t|
|
131
|
+
t.column :account_id, :integer
|
132
|
+
t.column :name, :string
|
133
|
+
end
|
134
|
+
|
109
135
|
create_distributed_table :accounts, :id
|
110
136
|
create_distributed_table :projects, :account_id
|
111
137
|
create_distributed_table :managers, :account_id
|
@@ -121,6 +147,7 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
121
147
|
create_distributed_table :allowed_places, :account_id
|
122
148
|
create_distributed_table :domains, :account_id
|
123
149
|
create_distributed_table :pages, :account_id
|
150
|
+
create_distributed_table :posts, :account_id
|
124
151
|
create_reference_table :categories
|
125
152
|
end
|
126
153
|
|
@@ -128,6 +155,7 @@ class Account < ActiveRecord::Base
|
|
128
155
|
multi_tenant :account
|
129
156
|
has_many :projects
|
130
157
|
has_one :manager, inverse_of: :account
|
158
|
+
has_many :optional_sub_tasks
|
131
159
|
end
|
132
160
|
|
133
161
|
class Project < ActiveRecord::Base
|
@@ -138,6 +166,7 @@ class Project < ActiveRecord::Base
|
|
138
166
|
|
139
167
|
has_many :project_categories
|
140
168
|
has_many :categories, through: :project_categories
|
169
|
+
has_and_belongs_to_many :managers
|
141
170
|
|
142
171
|
validates_uniqueness_of :name, scope: [:account]
|
143
172
|
end
|
@@ -145,12 +174,15 @@ end
|
|
145
174
|
class Manager < ActiveRecord::Base
|
146
175
|
multi_tenant :account
|
147
176
|
belongs_to :project
|
177
|
+
has_and_belongs_to_many :tasks, tenant_column: :account_id, tenant_enabled: true,
|
178
|
+
tenant_class_name: 'Account'
|
148
179
|
end
|
149
180
|
|
150
181
|
class Task < ActiveRecord::Base
|
151
182
|
multi_tenant :account
|
152
183
|
belongs_to :project
|
153
184
|
has_many :sub_tasks
|
185
|
+
has_and_belongs_to_many :managers, tenant_column: :account_id, tenant_enabled: true
|
154
186
|
|
155
187
|
validates_uniqueness_of :name
|
156
188
|
end
|
@@ -159,6 +191,14 @@ class SubTask < ActiveRecord::Base
|
|
159
191
|
multi_tenant :account
|
160
192
|
belongs_to :task
|
161
193
|
has_one :project, through: :task
|
194
|
+
has_many :optional_sub_tasks
|
195
|
+
end
|
196
|
+
|
197
|
+
with_belongs_to_required_by_default do
|
198
|
+
class OptionalSubTask < ActiveRecord::Base
|
199
|
+
multi_tenant :account, optional: true
|
200
|
+
belongs_to :sub_task
|
201
|
+
end
|
162
202
|
end
|
163
203
|
|
164
204
|
class StiSubTask < SubTask
|
@@ -194,10 +234,11 @@ end
|
|
194
234
|
class Comment < ActiveRecord::Base
|
195
235
|
multi_tenant :account
|
196
236
|
belongs_to :commentable, polymorphic: true
|
197
|
-
belongs_to :task, -> { where(comments: { commentable_type: 'Task'
|
237
|
+
belongs_to :task, -> { where(comments: { commentable_type: 'Task' }) }, foreign_key: 'commentable_id'
|
198
238
|
end
|
199
239
|
|
200
240
|
class Organization < ActiveRecord::Base
|
241
|
+
multi_tenant :organization
|
201
242
|
has_many :uuid_records
|
202
243
|
end
|
203
244
|
|
@@ -206,7 +247,7 @@ class UuidRecord < ActiveRecord::Base
|
|
206
247
|
end
|
207
248
|
|
208
249
|
class Category < ActiveRecord::Base
|
209
|
-
has_many
|
250
|
+
has_many :project_categories
|
210
251
|
has_many :projects, through: :project_categories
|
211
252
|
end
|
212
253
|
|