activerecord-multi-tenant 1.1.1 → 2.2.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/CI.yml +47 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Appraisals +6 -22
- data/CHANGELOG.md +46 -0
- data/README.md +1 -1
- data/activerecord-multi-tenant.gemspec +1 -2
- data/gemfiles/active_record_7.0.gemfile +8 -0
- data/gemfiles/rails_7.0.gemfile +8 -0
- data/lib/activerecord-multi-tenant/copy_from_client.rb +2 -2
- data/lib/activerecord-multi-tenant/migrations.rb +76 -9
- data/lib/activerecord-multi-tenant/model_extensions.rb +31 -8
- data/lib/activerecord-multi-tenant/multi_tenant.rb +57 -8
- data/lib/activerecord-multi-tenant/query_rewriter.rb +15 -11
- data/lib/activerecord-multi-tenant/sidekiq.rb +21 -1
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/lib/activerecord-multi-tenant.rb +0 -1
- data/spec/activerecord-multi-tenant/associations_spec.rb +21 -0
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +186 -54
- data/spec/activerecord-multi-tenant/query_rewriter_spec.rb +17 -0
- data/spec/activerecord-multi-tenant/record_modifications_spec.rb +19 -0
- data/spec/activerecord-multi-tenant/sidekiq_spec.rb +11 -0
- data/spec/schema.rb +50 -4
- data/spec/spec_helper.rb +7 -0
- metadata +13 -33
- data/.travis.yml +0 -34
- data/Gemfile.lock +0 -199
- data/gemfiles/active_record_5.2.gemfile +0 -16
- data/gemfiles/active_record_5.2.gemfile.lock +0 -188
- data/gemfiles/active_record_6.0.gemfile.lock +0 -198
- data/gemfiles/active_record_6.1.gemfile.lock +0 -198
- data/gemfiles/rails_5.2.gemfile +0 -16
- data/gemfiles/rails_5.2.gemfile.lock +0 -188
- data/gemfiles/rails_6.0.gemfile.lock +0 -198
- data/gemfiles/rails_6.1.gemfile.lock +0 -198
- data/lib/activerecord-multi-tenant/persistence_extension.rb +0 -13
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MultiTenant, 'Association methods' do
|
4
|
+
let(:account1) { Account.create! name: 'test1' }
|
5
|
+
let(:account2) { Account.create! name: 'test2' }
|
6
|
+
let(:project1) { Project.create! name: 'something1', account: account1 }
|
7
|
+
let(:project2) { Project.create! name: 'something2', account: account2, id: project1.id }
|
8
|
+
let(:task1) { Task.create! name: 'task1', project: project1, account: account1 }
|
9
|
+
let(:task2) { Task.create! name: 'task2', project: project2, account: account2, id: task1.id }
|
10
|
+
|
11
|
+
context 'include the tenant_id in queries and' do
|
12
|
+
it 'creates a task with correct account_id' do
|
13
|
+
expect(project2.tasks.create(name: 'task3').account_id).to eq(account2.id)
|
14
|
+
end
|
15
|
+
it 'return correct account_id' do
|
16
|
+
expect(task1.project.account_id).to_not eq(task2.project.account_id) # belongs_to
|
17
|
+
expect(project2.tasks.count).to eq(1)
|
18
|
+
expect(project2.tasks.first.account_id).to eq(account2.id) # has_many
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -70,6 +70,65 @@ describe MultiTenant do
|
|
70
70
|
it { expect(@partition_key_not_model_task.non_model_id).to be 77 }
|
71
71
|
end
|
72
72
|
|
73
|
+
|
74
|
+
describe 'Tenant model with a nonstandard class name' do
|
75
|
+
let(:account_klass) do
|
76
|
+
Class.new(ActiveRecord::Base) do
|
77
|
+
self.table_name = 'account'
|
78
|
+
def self.name
|
79
|
+
'UserAccount'
|
80
|
+
end
|
81
|
+
|
82
|
+
multi_tenant(:account)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
it "does not register the tenant model" do
|
86
|
+
expect(MultiTenant).not_to receive(:register_multi_tenant_model)
|
87
|
+
account_klass
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'Changes table_name after multi_tenant called' do
|
92
|
+
before do
|
93
|
+
account_klass.has_many(:posts, anonymous_class: post_klass)
|
94
|
+
post_klass.belongs_to(:account, anonymous_class: account_klass)
|
95
|
+
|
96
|
+
@account1 = account_klass.create! name: 'foo'
|
97
|
+
@account2 = account_klass.create! name: 'bar'
|
98
|
+
|
99
|
+
@post1 = @account1.posts.create! name: 'foobar'
|
100
|
+
@post2 = @account2.posts.create! name: 'baz'
|
101
|
+
|
102
|
+
MultiTenant.current_tenant = @account1
|
103
|
+
@posts = post_klass.all
|
104
|
+
end
|
105
|
+
|
106
|
+
let(:account_klass) do
|
107
|
+
Class.new(Account) do
|
108
|
+
def self.name
|
109
|
+
'Account'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
let(:post_klass) do
|
115
|
+
Class.new(ActiveRecord::Base) do
|
116
|
+
self.table_name = 'unknown'
|
117
|
+
|
118
|
+
multi_tenant(:account)
|
119
|
+
|
120
|
+
self.table_name = 'posts'
|
121
|
+
|
122
|
+
def self.name
|
123
|
+
'Post'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it { expect(@posts.length).to eq(1) }
|
129
|
+
it { expect(@posts).to eq([@post1]) }
|
130
|
+
end
|
131
|
+
|
73
132
|
# Scoping models
|
74
133
|
describe 'Project.all should be scoped to the current tenant if set' do
|
75
134
|
before do
|
@@ -144,6 +203,22 @@ describe MultiTenant do
|
|
144
203
|
end
|
145
204
|
end
|
146
205
|
|
206
|
+
it 'handles belongs_to with optional: true' do
|
207
|
+
record = OptionalSubTask.create(sub_task_id: sub_task.id)
|
208
|
+
expect(record.reload.sub_task).to eq(sub_task)
|
209
|
+
expect(record.account_id).to eq(nil)
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'handles changing tenant from nil to a value' do
|
213
|
+
record = OptionalSubTask.create(sub_task_id: sub_task.id)
|
214
|
+
expect(record.reload.sub_task).to eq(sub_task)
|
215
|
+
expect(record.account_id).to eq(nil)
|
216
|
+
|
217
|
+
record.account = account
|
218
|
+
record.save!
|
219
|
+
expect(record.reload.account_id).to eq(account.id)
|
220
|
+
end
|
221
|
+
|
147
222
|
it 'handles has_many through' do
|
148
223
|
MultiTenant.with(account) do
|
149
224
|
expect(project.sub_tasks).to eq [sub_task]
|
@@ -234,6 +309,55 @@ describe MultiTenant do
|
|
234
309
|
end
|
235
310
|
end
|
236
311
|
|
312
|
+
# Joins
|
313
|
+
describe 'joins for models' do
|
314
|
+
context 'for models with where condition in associations' do
|
315
|
+
let(:account) { Account.create!(name: 'Account 1') }
|
316
|
+
|
317
|
+
it 'should add tenant condition to the queries when tenant is set' do
|
318
|
+
expected_join_sql = <<-SQL.strip
|
319
|
+
SELECT "comments".* FROM "comments" INNER JOIN "tasks" ON "tasks"."id" = "comments"."commentable_id" AND "comments"."commentable_type" = 'Task' AND "tasks"."account_id" = 1 WHERE "comments"."account_id" = 1
|
320
|
+
SQL
|
321
|
+
|
322
|
+
MultiTenant.with(account) do
|
323
|
+
expect(Comment.joins(:task).to_sql).to eq(expected_join_sql)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'should add tenant condition to the queries when tenant is not set' do
|
328
|
+
MultiTenant.without do
|
329
|
+
expected_join_sql = <<-SQL.strip
|
330
|
+
SELECT "comments".* FROM "comments" INNER JOIN "tasks" ON "tasks"."id" = "comments"."commentable_id" AND "comments"."commentable_type" = 'Task' AND "comments"."account_id" = "tasks"."account_id"
|
331
|
+
SQL
|
332
|
+
expect(Comment.joins(:task).to_sql).to eq(expected_join_sql)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'for models with default associations' do
|
338
|
+
let(:account) { Account.create!(name: 'Account 1') }
|
339
|
+
|
340
|
+
it 'should add tenant condition to the queries when tenant is set' do
|
341
|
+
expected_join_sql = <<-SQL.strip
|
342
|
+
SELECT "projects".* FROM "projects" INNER JOIN "tasks" ON "tasks"."project_id" = "projects"."id" AND "tasks"."account_id" = 1 WHERE "projects"."account_id" = 1
|
343
|
+
SQL
|
344
|
+
|
345
|
+
MultiTenant.with(account) do
|
346
|
+
expect(Project.joins(:tasks).to_sql).to eq(expected_join_sql)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'should add tenant condition to the queries when tenant is not set' do
|
351
|
+
MultiTenant.without do
|
352
|
+
expected_join_sql = <<-SQL.strip
|
353
|
+
SELECT "projects".* FROM "projects" INNER JOIN "tasks" ON "tasks"."project_id" = "projects"."id" AND "projects"."account_id" = "tasks"."account_id"
|
354
|
+
SQL
|
355
|
+
expect(Project.joins(:tasks).to_sql).to eq(expected_join_sql)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
237
361
|
# ::with
|
238
362
|
describe "::with" do
|
239
363
|
it "should set current_tenant to the specified tenant inside the block" do
|
@@ -348,9 +472,12 @@ describe MultiTenant do
|
|
348
472
|
end
|
349
473
|
|
350
474
|
it "applies the team_id conditions in the where clause" do
|
351
|
-
|
352
|
-
|
353
|
-
|
475
|
+
option1 = <<-sql.strip
|
476
|
+
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "tasks"."account_id" = "sub_tasks"."account_id" WHERE "tasks"."project_id" = 1 AND "sub_tasks"."account_id" = 1 AND "tasks"."account_id" = 1
|
477
|
+
sql
|
478
|
+
option2 = <<-sql.strip
|
479
|
+
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "tasks"."account_id" = "sub_tasks"."account_id" WHERE "sub_tasks"."account_id" = 1 AND "tasks"."project_id" = 1 AND "tasks"."account_id" = 1
|
480
|
+
sql
|
354
481
|
|
355
482
|
account1 = Account.create! name: 'Account 1'
|
356
483
|
|
@@ -358,13 +485,13 @@ describe MultiTenant do
|
|
358
485
|
project1 = Project.create! name: 'Project 1'
|
359
486
|
task1 = Task.create! name: 'Task 1', project: project1
|
360
487
|
subtask1 = SubTask.create! task: task1
|
361
|
-
expect(project1.sub_tasks.to_sql).to eq(
|
488
|
+
expect(project1.sub_tasks.to_sql).to eq(option1).or(eq(option2))
|
362
489
|
expect(project1.sub_tasks).to include(subtask1)
|
363
490
|
end
|
364
491
|
|
365
492
|
MultiTenant.without do
|
366
493
|
expected_sql = <<-sql
|
367
|
-
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "
|
494
|
+
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "tasks"."account_id" = "sub_tasks"."account_id" WHERE "tasks"."project_id" = 1
|
368
495
|
sql
|
369
496
|
|
370
497
|
project = Project.first
|
@@ -373,9 +500,13 @@ describe MultiTenant do
|
|
373
500
|
end
|
374
501
|
|
375
502
|
it "tests joins between distributed and reference table" do
|
376
|
-
|
377
|
-
|
378
|
-
|
503
|
+
option1 = <<-sql.strip
|
504
|
+
SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."project_id" = 1 AND "project_categories"."account_id" = 1
|
505
|
+
sql
|
506
|
+
option2 = <<-sql.strip
|
507
|
+
SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."account_id" = 1 AND "project_categories"."project_id" = 1
|
508
|
+
sql
|
509
|
+
|
379
510
|
account1 = Account.create! name: 'Account 1'
|
380
511
|
category1 = Category.create! name: 'Category 1'
|
381
512
|
|
@@ -383,7 +514,7 @@ describe MultiTenant do
|
|
383
514
|
project1 = Project.create! name: 'Project 1'
|
384
515
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
385
516
|
|
386
|
-
expect(project1.categories.to_sql).to eq(
|
517
|
+
expect(project1.categories.to_sql).to eq(option1).or(eq(option2))
|
387
518
|
expect(project1.categories).to include(category1)
|
388
519
|
expect(project1.project_categories).to include(projectcategory)
|
389
520
|
end
|
@@ -398,7 +529,7 @@ describe MultiTenant do
|
|
398
529
|
expect(project.categories).to include(category1)
|
399
530
|
|
400
531
|
expected_sql = <<-sql
|
401
|
-
SELECT "projects".* FROM "projects" INNER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "
|
532
|
+
SELECT "projects".* FROM "projects" INNER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = "project_categories"."account_id" INNER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" WHERE "projects"."account_id" = 1
|
402
533
|
sql
|
403
534
|
|
404
535
|
expect(Project.where(account_id: 1).joins(:categories).to_sql).to eq(expected_sql.strip)
|
@@ -412,21 +543,18 @@ describe MultiTenant do
|
|
412
543
|
account1 = Account.create! name: 'Account 1'
|
413
544
|
category1 = Category.create! name: 'Category 1'
|
414
545
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."account_id" = 1 AND "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = 1 LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" AND "project_categories"."account_id" = 1 WHERE "projects"."account_id" = 1
|
422
|
-
sql
|
423
|
-
end
|
546
|
+
option1 = <<-sql.strip
|
547
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = 1 AND "projects"."account_id" = 1 LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" AND "project_categories"."account_id" = 1 WHERE "projects"."account_id" = 1
|
548
|
+
sql
|
549
|
+
option2 = <<-sql.strip
|
550
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."account_id" = 1 AND "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = 1 LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" AND "project_categories"."account_id" = 1 WHERE "projects"."account_id" = 1
|
551
|
+
sql
|
424
552
|
|
425
553
|
MultiTenant.with(account1) do
|
426
554
|
project1 = Project.create! name: 'Project 1'
|
427
555
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
428
556
|
|
429
|
-
expect(Project.eager_load(:categories).to_sql).to eq(
|
557
|
+
expect(Project.eager_load(:categories).to_sql).to eq(option1).or(eq(option2))
|
430
558
|
|
431
559
|
project = Project.eager_load(:categories).first
|
432
560
|
expect(project.categories).to include(category1)
|
@@ -435,7 +563,7 @@ describe MultiTenant do
|
|
435
563
|
|
436
564
|
MultiTenant.without do
|
437
565
|
expected_sql = <<-sql
|
438
|
-
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "
|
566
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = "project_categories"."account_id" LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" WHERE "projects"."account_id" = 1
|
439
567
|
sql
|
440
568
|
|
441
569
|
expect(Project.where(account_id: 1).eager_load(:categories).to_sql).to eq(expected_sql.strip)
|
@@ -451,26 +579,23 @@ describe MultiTenant do
|
|
451
579
|
category1 = Category.create! name: 'Category 1'
|
452
580
|
|
453
581
|
MultiTenant.with(account1) do
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."account_id" = 1 AND "projects"."id" = "tasks"."project_id" LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
|
461
|
-
sql
|
462
|
-
end
|
582
|
+
option1 = <<-sql.strip
|
583
|
+
SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "projects"."account_id" = 1 LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
|
584
|
+
sql
|
585
|
+
option2 = <<-sql.strip
|
586
|
+
SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."account_id" = 1 AND "projects"."id" = "tasks"."project_id" LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
|
587
|
+
sql
|
463
588
|
|
464
589
|
project1 = Project.create! name: 'Project 1'
|
465
590
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
466
591
|
|
467
592
|
project1.tasks.create! name: 'baz'
|
468
|
-
expect(Task.joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(
|
593
|
+
expect(Task.joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(option1).or(eq(option2))
|
469
594
|
end
|
470
595
|
|
471
596
|
MultiTenant.without do
|
472
597
|
expected_sql = <<-sql
|
473
|
-
SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "
|
598
|
+
SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "tasks"."account_id" = "projects"."account_id" LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
|
474
599
|
sql
|
475
600
|
|
476
601
|
expect(Task.where(account_id: 1).joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(expected_sql.strip)
|
@@ -485,32 +610,39 @@ describe MultiTenant do
|
|
485
610
|
project2 = Project.create! name: 'Project 2', account: Account.create!(name: 'Account2')
|
486
611
|
|
487
612
|
MultiTenant.with(account) do
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
613
|
+
option1 = <<-sql.strip
|
614
|
+
SELECT "projects".* FROM "projects" WHERE "projects"."account_id" = #{account.id} AND "projects"."id" = $1 LIMIT $2
|
615
|
+
sql
|
616
|
+
option2 = <<-sql.strip
|
617
|
+
SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 AND "projects"."account_id" = #{account.id} LIMIT $2
|
618
|
+
sql
|
619
|
+
option3 = <<-sql.strip
|
620
|
+
SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 AND "projects"."account_id" = #{account.id} LIMIT $2
|
621
|
+
sql
|
622
|
+
|
623
|
+
# Couldn't make the following line pass for some reason, so came up with an uglier alternative
|
624
|
+
# expect(Project).to receive(:find_by_sql).with(eq(option1).or(eq(option2)).or(eq(option3)), any_args).and_call_original
|
625
|
+
expect(Project).to receive(:find_by_sql).and_wrap_original do |m, *args|
|
626
|
+
expect(args[0]).to(eq(option1).or(eq(option2)).or(eq(option3)))
|
627
|
+
m.call(args[0], args[1], preparable:args[2][:preparable])
|
628
|
+
end
|
499
629
|
expect(Project.find(project.id)).to eq(project)
|
500
630
|
end
|
501
631
|
|
502
632
|
MultiTenant.without do
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
633
|
+
option1 = <<-sql.strip
|
634
|
+
SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2
|
635
|
+
sql
|
636
|
+
option2 = <<-sql.strip
|
637
|
+
SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2
|
638
|
+
sql
|
639
|
+
|
640
|
+
# Couldn't make the following line pass for some reason, so came up with an uglier alternative
|
641
|
+
# expect(Project).to receive(:find_by_sql).with(eq(option1).or(eq(option2)), any_args).and_call_original
|
642
|
+
expect(Project).to receive(:find_by_sql).and_wrap_original do |m, *args|
|
643
|
+
expect(args[0]).to(eq(option1).or(eq(option2)))
|
644
|
+
m.call(args[0], args[1], preparable:args[2][:preparable])
|
645
|
+
end
|
514
646
|
expect(Project.find(project2.id)).to eq(project2)
|
515
647
|
end
|
516
648
|
end
|
@@ -98,4 +98,21 @@ describe "Query Rewriter" do
|
|
98
98
|
}.not_to raise_error
|
99
99
|
end
|
100
100
|
end
|
101
|
+
|
102
|
+
context "when joining with a model with a default scope" do
|
103
|
+
let!(:account) { Account.create!(name: "Test Account") }
|
104
|
+
|
105
|
+
it "fetches only records within the default scope" do
|
106
|
+
alive = Domain.create(name: "alive", account: account)
|
107
|
+
deleted = Domain.create(name: "deleted", deleted: true, account: account)
|
108
|
+
page_in_alive_domain = Page.create(name: "alive", account: account, domain: alive)
|
109
|
+
page_in_deleted_domain = Page.create(name: "deleted", account: account, domain: deleted)
|
110
|
+
|
111
|
+
expect(
|
112
|
+
MultiTenant.with(account) do
|
113
|
+
Page.joins(:domain).pluck(:id)
|
114
|
+
end
|
115
|
+
).to eq([page_in_alive_domain.id])
|
116
|
+
end
|
117
|
+
end
|
101
118
|
end
|
@@ -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,6 +5,9 @@ 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(:deleted_acount) { Account.create(name: 'deleted') }
|
9
|
+
|
10
|
+
before { deleted_acount.destroy! }
|
8
11
|
|
9
12
|
describe 'server middleware' do
|
10
13
|
it 'sets the multitenant context when provided in message' do
|
@@ -15,6 +18,14 @@ describe MultiTenant, 'Sidekiq' do
|
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
21
|
+
it 'sets the multitenant context (id) even if tenant not found' do
|
22
|
+
server.call(double,{'bogus' => 'message',
|
23
|
+
'multi_tenant' => { 'class' => deleted_acount.class.name, 'id' => deleted_acount.id}},
|
24
|
+
'bogus_queue') do
|
25
|
+
expect(MultiTenant.current_tenant).to eq(deleted_acount.id)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
18
29
|
it 'does not set the multitenant context when no tenant provided' do
|
19
30
|
server.call(double, {'bogus' => 'message'}, 'bogus_queue') do
|
20
31
|
expect(MultiTenant.current_tenant).to be_nil
|
data/spec/schema.rb
CHANGED
@@ -34,6 +34,13 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
34
34
|
t.column :type, :string
|
35
35
|
end
|
36
36
|
|
37
|
+
create_table :optional_sub_tasks, force: true do |t|
|
38
|
+
t.references :account, :integer
|
39
|
+
t.column :sub_task_id, :integer
|
40
|
+
t.column :name, :string
|
41
|
+
t.column :type, :string
|
42
|
+
end
|
43
|
+
|
37
44
|
create_table :countries, force: true do |t|
|
38
45
|
t.column :name, :string
|
39
46
|
end
|
@@ -89,10 +96,26 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
89
96
|
t.column :category_id, :integer
|
90
97
|
end
|
91
98
|
|
92
|
-
|
93
99
|
create_table :allowed_places, force: true, id: false do |t|
|
94
|
-
|
95
|
-
|
100
|
+
t.string :account_id, :integer
|
101
|
+
t.string :name, :string
|
102
|
+
end
|
103
|
+
|
104
|
+
create_table :domains, force: true, partition_key: :account_id do |t|
|
105
|
+
t.column :account_id, :integer
|
106
|
+
t.column :name, :string
|
107
|
+
t.column :deleted, :boolean, default: false
|
108
|
+
end
|
109
|
+
|
110
|
+
create_table :pages, force: true, partition_key: :account_id do |t|
|
111
|
+
t.column :account_id, :integer
|
112
|
+
t.column :name, :string
|
113
|
+
t.column :domain_id, :integer
|
114
|
+
end
|
115
|
+
|
116
|
+
create_table :posts, force: true, partition_key: :account_id do |t|
|
117
|
+
t.column :account_id, :integer
|
118
|
+
t.column :name, :string
|
96
119
|
end
|
97
120
|
|
98
121
|
create_distributed_table :accounts, :id
|
@@ -108,6 +131,9 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
108
131
|
create_distributed_table :uuid_records, :organization_id
|
109
132
|
create_distributed_table :project_categories, :account_id
|
110
133
|
create_distributed_table :allowed_places, :account_id
|
134
|
+
create_distributed_table :domains, :account_id
|
135
|
+
create_distributed_table :pages, :account_id
|
136
|
+
create_distributed_table :posts, :account_id
|
111
137
|
create_reference_table :categories
|
112
138
|
end
|
113
139
|
|
@@ -115,6 +141,7 @@ class Account < ActiveRecord::Base
|
|
115
141
|
multi_tenant :account
|
116
142
|
has_many :projects
|
117
143
|
has_one :manager, inverse_of: :account
|
144
|
+
has_many :optional_sub_tasks
|
118
145
|
end
|
119
146
|
|
120
147
|
class Project < ActiveRecord::Base
|
@@ -146,6 +173,14 @@ class SubTask < ActiveRecord::Base
|
|
146
173
|
multi_tenant :account
|
147
174
|
belongs_to :task
|
148
175
|
has_one :project, through: :task
|
176
|
+
has_many :optional_sub_tasks
|
177
|
+
end
|
178
|
+
|
179
|
+
with_belongs_to_required_by_default do
|
180
|
+
class OptionalSubTask < ActiveRecord::Base
|
181
|
+
multi_tenant :account, optional: true
|
182
|
+
belongs_to :sub_task
|
183
|
+
end
|
149
184
|
end
|
150
185
|
|
151
186
|
class StiSubTask < SubTask
|
@@ -185,6 +220,7 @@ class Comment < ActiveRecord::Base
|
|
185
220
|
end
|
186
221
|
|
187
222
|
class Organization < ActiveRecord::Base
|
223
|
+
multi_tenant :organization
|
188
224
|
has_many :uuid_records
|
189
225
|
end
|
190
226
|
|
@@ -204,7 +240,17 @@ class ProjectCategory < ActiveRecord::Base
|
|
204
240
|
belongs_to :account
|
205
241
|
end
|
206
242
|
|
207
|
-
|
208
243
|
class AllowedPlace < ActiveRecord::Base
|
209
244
|
multi_tenant :account
|
210
245
|
end
|
246
|
+
|
247
|
+
class Domain < ActiveRecord::Base
|
248
|
+
multi_tenant :account
|
249
|
+
has_many :pages
|
250
|
+
default_scope { where(deleted: false) }
|
251
|
+
end
|
252
|
+
|
253
|
+
class Page < ActiveRecord::Base
|
254
|
+
multi_tenant :account
|
255
|
+
belongs_to :domain
|
256
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -46,4 +46,11 @@ def uses_prepared_statements?
|
|
46
46
|
ActiveRecord::Base.connection.prepared_statements
|
47
47
|
end
|
48
48
|
|
49
|
+
def with_belongs_to_required_by_default(&block)
|
50
|
+
default_value = ActiveRecord::Base.belongs_to_required_by_default
|
51
|
+
ActiveRecord::Base.belongs_to_required_by_default = true
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
ActiveRecord::Base.belongs_to_required_by_default = default_value
|
55
|
+
end
|
49
56
|
require 'schema'
|