scimitar 2.2.0 → 2.3.0

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: ce356c067dc4636a1540eec905c21730c6d37d7831725ed5fd914b0a62e7f1e7
4
- data.tar.gz: 979360b39861dc14e5d202ab580774e6164f9818b0358a8bcc47e06c6a6462c3
3
+ metadata.gz: 3904e389b3b00b4c49df6d9267cc0b5413613ed56b544ee888dd4b3bfa9d7489
4
+ data.tar.gz: 93b14676bda6d86d4add6c0bcbf8ae6415ff49177b384f2b75571a304485d87f
5
5
  SHA512:
6
- metadata.gz: 78eeeab146e8a18f265643bc99a32a461a7972b2971309421f4cbfa1751fabe0ad99a84037f8f2000ef5da90ae7282bf16c66fa34e26ed83e4ae2510248df651
7
- data.tar.gz: 15af818f707e5a7db410abd57fe342258c016fdf6fef79af12e384481f7730bdea7a22983e3fd662317e73437fdb0c491a651639479539a8029eebe3dd8d3ab6
6
+ metadata.gz: f1fad29d6ba090ad00b55bdf59f1050aacd1858d4e511e726d3fa501a37389e57777cd2cb2253c20972106e567a3f4b5e07ecd53a17723dcab2517f0c8ab99cf
7
+ data.tar.gz: 30a1c3ad4c6653b347cf00228cad99b88a3dc4a2da45fe311b61d0a0f2d08db723fabf2041e3d72f1d1cea28d633c540322394841723bdd8db7d805e49819eb4
@@ -21,6 +21,8 @@ module Scimitar
21
21
 
22
22
  rescue_from ActiveRecord::RecordNotFound, with: :handle_resource_not_found # See Scimitar::ApplicationController
23
23
 
24
+ before_action :obtain_id_column_name_from_attribute_map
25
+
24
26
  # GET (list)
25
27
  #
26
28
  def index
@@ -37,13 +39,13 @@ module Scimitar
37
39
  pagination_info = scim_pagination_info(query.count())
38
40
 
39
41
  page_of_results = query
40
- .order(id: :asc)
42
+ .order(@id_column => :asc)
41
43
  .offset(pagination_info.offset)
42
44
  .limit(pagination_info.limit)
43
45
  .to_a()
44
46
 
45
47
  super(pagination_info, page_of_results) do | record |
46
- record.to_scim(location: url_for(action: :show, id: record.id))
48
+ record_to_scim(record)
47
49
  end
48
50
  end
49
51
 
@@ -52,7 +54,7 @@ module Scimitar
52
54
  def show
53
55
  super do |record_id|
54
56
  record = self.find_record(record_id)
55
- record.to_scim(location: url_for(action: :show, id: record_id))
57
+ record_to_scim(record)
56
58
  end
57
59
  end
58
60
 
@@ -64,7 +66,7 @@ module Scimitar
64
66
  record = self.storage_class().new
65
67
  record.from_scim!(scim_hash: scim_resource.as_json())
66
68
  self.save!(record)
67
- record.to_scim(location: url_for(action: :show, id: record.id))
69
+ record_to_scim(record)
68
70
  end
69
71
  end
70
72
  end
@@ -77,7 +79,7 @@ module Scimitar
77
79
  record = self.find_record(record_id)
78
80
  record.from_scim!(scim_hash: scim_resource.as_json())
79
81
  self.save!(record)
80
- record.to_scim(location: url_for(action: :show, id: record.id))
82
+ record_to_scim(record)
81
83
  end
82
84
  end
83
85
  end
@@ -90,7 +92,7 @@ module Scimitar
90
92
  record = self.find_record(record_id)
91
93
  record.from_scim_patch!(patch_hash: patch_hash)
92
94
  self.save!(record)
93
- record.to_scim(location: url_for(action: :show, id: record.id))
95
+ record_to_scim(record)
94
96
  end
95
97
  end
96
98
  end
@@ -138,7 +140,14 @@ module Scimitar
138
140
  # +record_id+:: Record ID (SCIM schema 'id' value - "our" ID).
139
141
  #
140
142
  def find_record(record_id)
141
- self.storage_scope().find(record_id)
143
+ self.storage_scope().find_by!(@id_column => record_id)
144
+ end
145
+
146
+ # DRY up controller actions - pass a record; returns the SCIM
147
+ # representation, with a "show" location specified via #url_for.
148
+ #
149
+ def record_to_scim(record)
150
+ record.to_scim(location: url_for(action: :show, id: record.send(@id_column)))
142
151
  end
143
152
 
144
153
  # Save a record, dealing with validation exceptions by raising SCIM
@@ -177,5 +186,16 @@ module Scimitar
177
186
  end
178
187
  end
179
188
 
189
+ # Called via +before_action+ - stores in @id_column the name of whatever
190
+ # model column is used to store the record ID, via
191
+ # Scimitar::Resources::Mixin::scim_attributes_map.
192
+ #
193
+ # Default is <tt>:id</tt>.
194
+ #
195
+ def obtain_id_column_name_from_attribute_map
196
+ attrs = storage_class().scim_attributes_map() || {}
197
+ @id_column = attrs[:id] || :id
198
+ end
199
+
180
200
  end
181
201
  end
@@ -3,11 +3,11 @@ module Scimitar
3
3
  # Gem version. If this changes, be sure to re-run "bundle install" or
4
4
  # "bundle update".
5
5
  #
6
- VERSION = '2.2.0'
6
+ VERSION = '2.3.0'
7
7
 
8
8
  # Date for VERSION. If this changes, be sure to re-run "bundle install"
9
9
  # or "bundle update".
10
10
  #
11
- DATE = '2023-01-13'
11
+ DATE = '2023-01-17'
12
12
 
13
13
  end
@@ -1,4 +1,4 @@
1
- class MockUsersController < Scimitar::ActiveRecordBackedResourcesController
1
+ class MockGroupsController < Scimitar::ActiveRecordBackedResourcesController
2
2
 
3
3
  protected
4
4
 
@@ -58,7 +58,7 @@ class MockGroup < ActiveRecord::Base
58
58
 
59
59
  case type.downcase
60
60
  when 'user'
61
- MockUser.find_by_id(id)
61
+ MockUser.find_by_primary_key(id)
62
62
  when 'group'
63
63
  MockGroup.find_by_id(id)
64
64
  else
@@ -1,11 +1,13 @@
1
1
  class MockUser < ActiveRecord::Base
2
2
 
3
+ self.primary_key = :primary_key
4
+
3
5
  # ===========================================================================
4
6
  # TEST ATTRIBUTES - see db/migrate/20210304014602_create_mock_users.rb etc.
5
7
  # ===========================================================================
6
8
 
7
9
  READWRITE_ATTRS = %w{
8
- id
10
+ primary_key
9
11
  scim_uid
10
12
  username
11
13
  first_name
@@ -38,7 +40,7 @@ class MockUser < ActiveRecord::Base
38
40
 
39
41
  def self.scim_attributes_map
40
42
  return {
41
- id: :id,
43
+ id: :primary_key,
42
44
  externalId: :scim_uid,
43
45
  userName: :username,
44
46
  name: {
@@ -92,7 +94,7 @@ class MockUser < ActiveRecord::Base
92
94
 
93
95
  def self.scim_queryable_attributes
94
96
  return {
95
- 'id' => { column: :id },
97
+ 'id' => { column: :primary_key },
96
98
  'externalId' => { column: :scim_uid },
97
99
  'meta.lastModified' => { column: :updated_at },
98
100
  'name.givenName' => { column: :first_name },
@@ -6,12 +6,15 @@
6
6
  Rails.application.routes.draw do
7
7
  mount Scimitar::Engine, at: '/'
8
8
 
9
- get 'Users', to: 'mock_users#index'
10
- get 'Users/:id', to: 'mock_users#show'
11
- post 'Users', to: 'mock_users#create'
12
- put 'Users/:id', to: 'mock_users#replace'
13
- patch 'Users/:id', to: 'mock_users#update'
14
- delete 'Users/:id', to: 'mock_users#destroy'
9
+ get 'Users', to: 'mock_users#index'
10
+ get 'Users/:id', to: 'mock_users#show'
11
+ post 'Users', to: 'mock_users#create'
12
+ put 'Users/:id', to: 'mock_users#replace'
13
+ patch 'Users/:id', to: 'mock_users#update'
14
+ delete 'Users/:id', to: 'mock_users#destroy'
15
+
16
+ get 'Groups', to: 'mock_groups#index'
17
+ get 'Groups/:id', to: 'mock_groups#show'
15
18
 
16
19
  # For testing blocks passed to ActiveRecordBackedResourcesController#destroy
17
20
  #
@@ -1,6 +1,7 @@
1
1
  class CreateMockUsers < ActiveRecord::Migration[6.1]
2
2
  def change
3
- create_table :mock_users do |t|
3
+ create_table :mock_users, id: :uuid, primary_key: :primary_key do |t|
4
+ t.timestamps
4
5
 
5
6
  t.text :scim_uid
6
7
  t.text :username
@@ -9,7 +10,6 @@ class CreateMockUsers < ActiveRecord::Migration[6.1]
9
10
  t.text :work_email_address
10
11
  t.text :home_email_address
11
12
  t.text :work_phone_number
12
-
13
13
  end
14
14
  end
15
15
  end
@@ -1,8 +1,13 @@
1
1
  class CreateJoinTableMockGroupsMockUsers < ActiveRecord::Migration[6.1]
2
2
  def change
3
- create_join_table :mock_groups, :mock_users do |t|
4
- t.index [:mock_group_id, :mock_user_id]
5
- t.index [:mock_user_id, :mock_group_id]
3
+ create_table :mock_groups_users, id: false do | t |
4
+ t.references :mock_group, foreign_key: true, type: :int8, index: true, null: false
5
+ t.references :mock_user, type: :uuid, index: true, null: false, primary_key: :primary_key
6
+
7
+ # The 'foreign_key:' option (used above) only works for 'id' column names
8
+ # but the test data has a column named 'primary_key' for 'mock_users'.
9
+ #
10
+ t.foreign_key :mock_users, primary_key: :primary_key
6
11
  end
7
12
  end
8
13
  end
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema[7.0].define(version: 2023_01_09_012729) do
13
+ ActiveRecord::Schema[7.0].define(version: 2021_03_08_044214) do
14
14
  # These are extensions that must be enabled in order to support this database
15
15
  enable_extension "plpgsql"
16
16
 
@@ -23,12 +23,14 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_09_012729) do
23
23
 
24
24
  create_table "mock_groups_users", id: false, force: :cascade do |t|
25
25
  t.bigint "mock_group_id", null: false
26
- t.bigint "mock_user_id", null: false
27
- t.index ["mock_group_id", "mock_user_id"], name: "index_mock_groups_users_on_mock_group_id_and_mock_user_id"
28
- t.index ["mock_user_id", "mock_group_id"], name: "index_mock_groups_users_on_mock_user_id_and_mock_group_id"
26
+ t.uuid "mock_user_id", null: false
27
+ t.index ["mock_group_id"], name: "index_mock_groups_users_on_mock_group_id"
28
+ t.index ["mock_user_id"], name: "index_mock_groups_users_on_mock_user_id"
29
29
  end
30
30
 
31
- create_table "mock_users", force: :cascade do |t|
31
+ create_table "mock_users", primary_key: "primary_key", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
32
+ t.datetime "created_at", null: false
33
+ t.datetime "updated_at", null: false
32
34
  t.text "scim_uid"
33
35
  t.text "username"
34
36
  t.text "first_name"
@@ -36,8 +38,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_09_012729) do
36
38
  t.text "work_email_address"
37
39
  t.text "home_email_address"
38
40
  t.text "work_phone_number"
39
- t.datetime "created_at", null: false
40
- t.datetime "updated_at", null: false
41
41
  end
42
42
 
43
+ add_foreign_key "mock_groups_users", "mock_groups"
44
+ add_foreign_key "mock_groups_users", "mock_users", primary_key: "primary_key"
43
45
  end
@@ -405,19 +405,19 @@ RSpec.describe Scimitar::Lists::QueryParser do
405
405
  query = @instance.to_activerecord_query(MockUser.all)
406
406
 
407
407
  expect(query.count).to eql(1)
408
- expect(query.pluck(:id)).to eql([user_1.id])
408
+ expect(query.pluck(:primary_key)).to eql([user_1.primary_key])
409
409
 
410
410
  @instance.parse('name.givenName sw J') # First name starts with 'J'
411
411
  query = @instance.to_activerecord_query(MockUser.all)
412
412
 
413
413
  expect(query.count).to eql(2)
414
- expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
414
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key, user_2.primary_key])
415
415
 
416
416
  @instance.parse('name.familyName ew he') # Last name ends with 'he'
417
417
  query = @instance.to_activerecord_query(MockUser.all)
418
418
 
419
419
  expect(query.count).to eql(1)
420
- expect(query.pluck(:id)).to eql([user_2.id])
420
+ expect(query.pluck(:primary_key)).to eql([user_2.primary_key])
421
421
 
422
422
  # Test presence
423
423
 
@@ -425,7 +425,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
425
425
  query = @instance.to_activerecord_query(MockUser.all)
426
426
 
427
427
  expect(query.count).to eql(2)
428
- expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
428
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key, user_2.primary_key])
429
429
 
430
430
  # Test a simple not-equals, but use a custom starting scope. Note that
431
431
  # the query would find "user_3" *except* there is no first name defined
@@ -435,7 +435,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
435
435
  query = @instance.to_activerecord_query(MockUser.where.not('first_name' => 'John'))
436
436
 
437
437
  expect(query.count).to eql(1)
438
- expect(query.pluck(:id)).to match_array([user_1.id])
438
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key])
439
439
  end
440
440
 
441
441
  context 'when mapped to multiple columns' do
@@ -499,7 +499,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
499
499
  query = @instance.to_activerecord_query(MockUser.all)
500
500
 
501
501
  expect(query.count).to eql(1)
502
- expect(query.pluck(:id)).to match_array([user_2.id])
502
+ expect(query.pluck(:primary_key)).to match_array([user_2.primary_key])
503
503
  end
504
504
  end # "context 'simple AND' do"
505
505
 
@@ -520,7 +520,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
520
520
  query = @instance.to_activerecord_query(MockUser.all)
521
521
 
522
522
  expect(query.count).to eql(2)
523
- expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
523
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key, user_2.primary_key])
524
524
  end
525
525
  end # "context 'simple OR' do"
526
526
 
@@ -546,7 +546,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
546
546
  query = @instance.to_activerecord_query(MockUser.all)
547
547
 
548
548
  expect(query.count).to eql(3)
549
- expect(query.pluck(:id)).to match_array([user_1.id, user_2.id, user_3.id])
549
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key, user_2.primary_key, user_3.primary_key])
550
550
  end
551
551
  end # "context 'combined AND and OR' do"
552
552
 
@@ -159,41 +159,67 @@ RSpec.describe Scimitar::Resources::Mixin do
159
159
  # =========================================================================
160
160
 
161
161
  context '#to_scim' do
162
- it 'compiles instance attribute values into a SCIM representation' do
163
- instance = MockUser.new
164
- instance.id = 42
165
- instance.scim_uid = 'AA02984'
166
- instance.username = 'foo'
167
- instance.first_name = 'Foo'
168
- instance.last_name = 'Bar'
169
- instance.work_email_address = 'foo.bar@test.com'
170
- instance.home_email_address = nil
171
- instance.work_phone_number = '+642201234567'
172
-
173
- g1 = MockGroup.create!(display_name: 'Group 1')
174
- g2 = MockGroup.create!(display_name: 'Group 2')
175
- g3 = MockGroup.create!(display_name: 'Group 3')
176
-
177
- g1.mock_users << instance
178
- g3.mock_users << instance
179
-
180
- scim = instance.to_scim(location: 'https://test.com/mock_users/42')
181
- json = scim.to_json()
182
- hash = JSON.parse(json)
183
-
184
- expect(hash).to eql({
185
- 'userName' => 'foo',
186
- 'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
187
- 'active' => true,
188
- 'emails' => [{'type'=>'work', 'primary'=>true, 'value'=>'foo.bar@test.com'}, {"primary"=>false, "type"=>"home", "value"=>nil}],
189
- 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567'}],
190
- 'id' => '42', # Note, String
191
- 'externalId' => 'AA02984',
192
- 'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
193
- 'meta' => {'location'=>'https://test.com/mock_users/42', 'resourceType'=>'User'},
194
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
195
- })
196
- end
162
+ context 'with a UUID, renamed primary key column' do
163
+ it 'compiles instance attribute values into a SCIM representation' do
164
+ uuid = SecureRandom.uuid
165
+
166
+ instance = MockUser.new
167
+ instance.primary_key = uuid
168
+ instance.scim_uid = 'AA02984'
169
+ instance.username = 'foo'
170
+ instance.first_name = 'Foo'
171
+ instance.last_name = 'Bar'
172
+ instance.work_email_address = 'foo.bar@test.com'
173
+ instance.home_email_address = nil
174
+ instance.work_phone_number = '+642201234567'
175
+
176
+ g1 = MockGroup.create!(display_name: 'Group 1')
177
+ g2 = MockGroup.create!(display_name: 'Group 2')
178
+ g3 = MockGroup.create!(display_name: 'Group 3')
179
+
180
+ g1.mock_users << instance
181
+ g3.mock_users << instance
182
+
183
+ scim = instance.to_scim(location: "https://test.com/mock_users/#{uuid}")
184
+ json = scim.to_json()
185
+ hash = JSON.parse(json)
186
+
187
+ expect(hash).to eql({
188
+ 'userName' => 'foo',
189
+ 'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
190
+ 'active' => true,
191
+ 'emails' => [{'type'=>'work', 'primary'=>true, 'value'=>'foo.bar@test.com'}, {"primary"=>false, "type"=>"home", "value"=>nil}],
192
+ 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567'}],
193
+ 'id' => uuid,
194
+ 'externalId' => 'AA02984',
195
+ 'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
196
+ 'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'},
197
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
198
+ })
199
+ end
200
+ end # "context 'with a UUID, renamed primary key column' do"
201
+
202
+ context 'with an integer, conventionally named primary key column' do
203
+ it 'compiles instance attribute values into a SCIM representation' do
204
+ instance = MockGroup.new
205
+ instance.id = 42
206
+ instance.scim_uid = 'GG02984'
207
+ instance.display_name = 'Some group'
208
+
209
+ scim = instance.to_scim(location: 'https://test.com/mock_groups/42')
210
+ json = scim.to_json()
211
+ hash = JSON.parse(json)
212
+
213
+ expect(hash).to eql({
214
+ 'displayName' => 'Some group',
215
+ 'id' => '42', # Note, String
216
+ 'externalId' => 'GG02984',
217
+ 'members' => [],
218
+ 'meta' => {'location'=>'https://test.com/mock_groups/42', 'resourceType'=>'Group'},
219
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group']
220
+ })
221
+ end
222
+ end # "context 'with an integer, conventionally named primary key column' do"
197
223
 
198
224
  context 'with optional timestamps' do
199
225
  context 'creation only' do
@@ -405,8 +431,8 @@ RSpec.describe Scimitar::Resources::Mixin do
405
431
  'displayName' => 'Foo Group',
406
432
  'members' => [
407
433
  {'type' => 'Group', 'value' => g1.id.to_s},
408
- {'type' => 'User', 'value' => u1.id.to_s},
409
- {'type' => 'User', 'value' => u3.id.to_s}
434
+ {'type' => 'User', 'value' => u1.primary_key.to_s},
435
+ {'type' => 'User', 'value' => u3.primary_key.to_s}
410
436
  ],
411
437
  'externalId' => 'GG01536',
412
438
  'meta' => {'location'=>'https://test.com/mock_groups/1', 'resourceType'=>'Group'},
@@ -453,8 +479,10 @@ RSpec.describe Scimitar::Resources::Mixin do
453
479
  end # "context 'using upper case' do"
454
480
 
455
481
  it 'clears things not present in input' do
482
+ uuid = SecureRandom.uuid
483
+
456
484
  instance = MockUser.new
457
- instance.id = 42
485
+ instance.primary_key = uuid
458
486
  instance.scim_uid = 'AA02984'
459
487
  instance.username = 'foo'
460
488
  instance.first_name = 'Foo'
@@ -465,7 +493,7 @@ RSpec.describe Scimitar::Resources::Mixin do
465
493
 
466
494
  instance.from_scim!(scim_hash: {})
467
495
 
468
- expect(instance.id ).to eql(42)
496
+ expect(instance.primary_key ).to eql(uuid)
469
497
  expect(instance.scim_uid ).to be_nil
470
498
  expect(instance.username ).to be_nil
471
499
  expect(instance.first_name ).to be_nil
@@ -5,11 +5,21 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
5
5
  before :each do
6
6
  allow_any_instance_of(Scimitar::ApplicationController).to receive(:authenticated?).and_return(true)
7
7
 
8
+ # If a sort order is unspecified, the controller defaults to ID ascending.
9
+ # With UUID based IDs, testing life is made easier by ensuring that the
10
+ # creation order matches an ascending UUID sort order (which is what would
11
+ # happen if we were using integer primary keys).
12
+ #
8
13
  lmt = Time.parse("2023-01-09 14:25:00 +1300")
14
+ ids = 3.times.map { SecureRandom.uuid }.sort()
9
15
 
10
- @u1 = MockUser.create(username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com', scim_uid: '001', created_at: lmt, updated_at: lmt + 1)
11
- @u2 = MockUser.create(username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com', scim_uid: '002', created_at: lmt, updated_at: lmt + 2)
12
- @u3 = MockUser.create(username: '3', first_name: 'Foo', home_email_address: 'home_3@test.com', scim_uid: '003', created_at: lmt, updated_at: lmt + 3)
16
+ @u1 = MockUser.create(primary_key: ids.shift(), username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com', scim_uid: '001', created_at: lmt, updated_at: lmt + 1)
17
+ @u2 = MockUser.create(primary_key: ids.shift(), username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com', scim_uid: '002', created_at: lmt, updated_at: lmt + 2)
18
+ @u3 = MockUser.create(primary_key: ids.shift(), username: '3', first_name: 'Foo', home_email_address: 'home_3@test.com', scim_uid: '003', created_at: lmt, updated_at: lmt + 3)
19
+
20
+ @g1 = MockGroup.create!(display_name: 'Group 1')
21
+ @g2 = MockGroup.create!(display_name: 'Group 2')
22
+ @g3 = MockGroup.create!(display_name: 'Group 3')
13
23
  end
14
24
 
15
25
  # ===========================================================================
@@ -32,21 +42,41 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
32
42
  end # "context 'with no items' do"
33
43
 
34
44
  context 'with items' do
35
- it 'returns all items' do
36
- get '/Users', params: { format: :scim }
45
+ context 'with a UUID, renamed primary key column' do
46
+ it 'returns all items' do
47
+ get '/Users', params: { format: :scim }
37
48
 
38
- expect(response.status).to eql(200)
39
- result = JSON.parse(response.body)
49
+ expect(response.status).to eql(200)
50
+ result = JSON.parse(response.body)
40
51
 
41
- expect(result['totalResults']).to eql(3)
42
- expect(result['Resources'].size).to eql(3)
52
+ expect(result['totalResults']).to eql(3)
53
+ expect(result['Resources'].size).to eql(3)
43
54
 
44
- ids = result['Resources'].map { |resource| resource['id'] }
45
- expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s, @u3.id.to_s])
55
+ ids = result['Resources'].map { |resource| resource['id'] }
56
+ expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s, @u3.primary_key.to_s])
46
57
 
47
- usernames = result['Resources'].map { |resource| resource['userName'] }
48
- expect(usernames).to match_array(['1', '2', '3'])
49
- end
58
+ usernames = result['Resources'].map { |resource| resource['userName'] }
59
+ expect(usernames).to match_array(['1', '2', '3'])
60
+ end
61
+ end # "context 'with a UUID, renamed primary key column' do"
62
+
63
+ context 'with an integer, conventionally named primary key column' do
64
+ it 'returns all items' do
65
+ get '/Groups', params: { format: :scim }
66
+
67
+ expect(response.status).to eql(200)
68
+ result = JSON.parse(response.body)
69
+
70
+ expect(result['totalResults']).to eql(3)
71
+ expect(result['Resources'].size).to eql(3)
72
+
73
+ ids = result['Resources'].map { |resource| resource['id'] }
74
+ expect(ids).to match_array([@g1.id.to_s, @g2.id.to_s, @g3.id.to_s])
75
+
76
+ usernames = result['Resources'].map { |resource| resource['displayName'] }
77
+ expect(usernames).to match_array(['Group 1', 'Group 2', 'Group 3'])
78
+ end
79
+ end # "context 'with an integer, conventionally named primary key column' do"
50
80
 
51
81
  it 'applies a filter, with case-insensitive value comparison' do
52
82
  get '/Users', params: {
@@ -61,7 +91,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
61
91
  expect(result['Resources'].size).to eql(1)
62
92
 
63
93
  ids = result['Resources'].map { |resource| resource['id'] }
64
- expect(ids).to match_array([@u2.id.to_s])
94
+ expect(ids).to match_array([@u2.primary_key.to_s])
65
95
 
66
96
  usernames = result['Resources'].map { |resource| resource['userName'] }
67
97
  expect(usernames).to match_array(['2'])
@@ -80,7 +110,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
80
110
  expect(result['Resources'].size).to eql(1)
81
111
 
82
112
  ids = result['Resources'].map { |resource| resource['id'] }
83
- expect(ids).to match_array([@u2.id.to_s])
113
+ expect(ids).to match_array([@u2.primary_key.to_s])
84
114
 
85
115
  usernames = result['Resources'].map { |resource| resource['userName'] }
86
116
  expect(usernames).to match_array(['2'])
@@ -93,7 +123,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
93
123
  it 'applies a filter on primary keys, using direct comparison (rather than e.g. case-insensitive operators)' do
94
124
  get '/Users', params: {
95
125
  format: :scim,
96
- filter: "id eq \"#{@u3.id}\""
126
+ filter: "id eq \"#{@u3.primary_key}\""
97
127
  }
98
128
 
99
129
  expect(response.status).to eql(200)
@@ -103,7 +133,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
103
133
  expect(result['Resources'].size).to eql(1)
104
134
 
105
135
  ids = result['Resources'].map { |resource| resource['id'] }
106
- expect(ids).to match_array([@u3.id.to_s])
136
+ expect(ids).to match_array([@u3.primary_key.to_s])
107
137
 
108
138
  usernames = result['Resources'].map { |resource| resource['userName'] }
109
139
  expect(usernames).to match_array(['3'])
@@ -122,7 +152,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
122
152
  expect(result['Resources'].size).to eql(1)
123
153
 
124
154
  ids = result['Resources'].map { |resource| resource['id'] }
125
- expect(ids).to match_array([@u2.id.to_s])
155
+ expect(ids).to match_array([@u2.primary_key.to_s])
126
156
 
127
157
  usernames = result['Resources'].map { |resource| resource['userName'] }
128
158
  expect(usernames).to match_array(['2'])
@@ -141,7 +171,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
141
171
  expect(result['Resources'].size).to eql(1)
142
172
 
143
173
  ids = result['Resources'].map { |resource| resource['id'] }
144
- expect(ids).to match_array([@u3.id.to_s])
174
+ expect(ids).to match_array([@u3.primary_key.to_s])
145
175
 
146
176
  usernames = result['Resources'].map { |resource| resource['userName'] }
147
177
  expect(usernames).to match_array(['3'])
@@ -161,7 +191,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
161
191
  expect(result['Resources'].size).to eql(2)
162
192
 
163
193
  ids = result['Resources'].map { |resource| resource['id'] }
164
- expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s])
194
+ expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s])
165
195
 
166
196
  usernames = result['Resources'].map { |resource| resource['userName'] }
167
197
  expect(usernames).to match_array(['1', '2'])
@@ -180,7 +210,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
180
210
  expect(result['Resources'].size).to eql(2)
181
211
 
182
212
  ids = result['Resources'].map { |resource| resource['id'] }
183
- expect(ids).to match_array([@u2.id.to_s, @u3.id.to_s])
213
+ expect(ids).to match_array([@u2.primary_key.to_s, @u3.primary_key.to_s])
184
214
 
185
215
  usernames = result['Resources'].map { |resource| resource['userName'] }
186
216
  expect(usernames).to match_array(['2', '3'])
@@ -204,18 +234,34 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
204
234
  # ===========================================================================
205
235
 
206
236
  context '#show' do
207
- it 'shows an item' do
208
- expect_any_instance_of(MockUsersController).to receive(:show).once.and_call_original
209
- get "/Users/#{@u2.id}", params: { format: :scim }
237
+ context 'with a UUID, renamed primary key column' do
238
+ it 'shows an item' do
239
+ expect_any_instance_of(MockUsersController).to receive(:show).once.and_call_original
240
+ get "/Users/#{@u2.primary_key}", params: { format: :scim }
210
241
 
211
- expect(response.status).to eql(200)
212
- result = JSON.parse(response.body)
242
+ expect(response.status).to eql(200)
243
+ result = JSON.parse(response.body)
213
244
 
214
- expect(result['id']).to eql(@u2.id.to_s) # Note - ID was converted String; not Integer
215
- expect(result['userName']).to eql('2')
216
- expect(result['name']['familyName']).to eql('Bar')
217
- expect(result['meta']['resourceType']).to eql('User')
218
- end
245
+ expect(result['id']).to eql(@u2.primary_key.to_s)
246
+ expect(result['userName']).to eql('2')
247
+ expect(result['name']['familyName']).to eql('Bar')
248
+ expect(result['meta']['resourceType']).to eql('User')
249
+ end
250
+ end # "context 'with a UUID, renamed primary key column' do"
251
+
252
+ context 'with an integer, conventionally named primary key column' do
253
+ it 'shows an item' do
254
+ expect_any_instance_of(MockGroupsController).to receive(:show).once.and_call_original
255
+ get "/Groups/#{@g2.id}", params: { format: :scim }
256
+
257
+ expect(response.status).to eql(200)
258
+ result = JSON.parse(response.body)
259
+
260
+ expect(result['id']).to eql(@g2.id.to_s) # Note - ID was converted String; not Integer
261
+ expect(result['displayName']).to eql('Group 2')
262
+ expect(result['meta']['resourceType']).to eql('Group')
263
+ end
264
+ end # "context 'with an integer, conventionally named primary key column' do"
219
265
 
220
266
  it 'renders 404' do
221
267
  get '/Users/xyz', params: { format: :scim }
@@ -234,7 +280,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
234
280
  it 'with minimal parameters' do
235
281
  mock_before = MockUser.all.to_a
236
282
 
237
- attributes = { userName: '4' } # Minimum required by schema
283
+ attributes = { userName: '4' } # Minimum required by schema
238
284
  attributes = spec_helper_hupcase(attributes) if force_upper_case
239
285
 
240
286
  expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
@@ -248,7 +294,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
248
294
  expect(response.status).to eql(201)
249
295
  result = JSON.parse(response.body)
250
296
 
251
- expect(result['id']).to eql(new_mock.id.to_s)
297
+ expect(result['id']).to eql(new_mock.primary_key.to_s)
252
298
  expect(result['meta']['resourceType']).to eql('User')
253
299
  expect(new_mock.username).to eql('4')
254
300
  end
@@ -363,13 +409,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
363
409
 
364
410
  expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
365
411
  expect {
366
- put "/Users/#{@u2.id}", params: attributes.merge(format: :scim)
412
+ put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
367
413
  }.to_not change { MockUser.count }
368
414
 
369
415
  expect(response.status).to eql(200)
370
416
  result = JSON.parse(response.body)
371
417
 
372
- expect(result['id']).to eql(@u2.id.to_s)
418
+ expect(result['id']).to eql(@u2.primary_key.to_s)
373
419
  expect(result['meta']['resourceType']).to eql('User')
374
420
 
375
421
  @u2.reload
@@ -391,7 +437,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
391
437
 
392
438
  it 'notes schema validation failures' do
393
439
  expect {
394
- put "/Users/#{@u2.id}", params: {
440
+ put "/Users/#{@u2.primary_key}", params: {
395
441
  format: :scim
396
442
  # userName parameter is required by schema, but missing
397
443
  }
@@ -470,13 +516,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
470
516
 
471
517
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
472
518
  expect {
473
- patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
519
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
474
520
  }.to_not change { MockUser.count }
475
521
 
476
522
  expect(response.status).to eql(200)
477
523
  result = JSON.parse(response.body)
478
524
 
479
- expect(result['id']).to eql(@u2.id.to_s)
525
+ expect(result['id']).to eql(@u2.primary_key.to_s)
480
526
  expect(result['meta']['resourceType']).to eql('User')
481
527
 
482
528
  @u2.reload
@@ -507,13 +553,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
507
553
 
508
554
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
509
555
  expect {
510
- patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
556
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
511
557
  }.to_not change { MockUser.count }
512
558
 
513
559
  expect(response.status).to eql(200)
514
560
  result = JSON.parse(response.body)
515
561
 
516
- expect(result['id']).to eql(@u2.id.to_s)
562
+ expect(result['id']).to eql(@u2.primary_key.to_s)
517
563
  expect(result['meta']['resourceType']).to eql('User')
518
564
 
519
565
  @u2.reload
@@ -539,13 +585,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
539
585
 
540
586
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
541
587
  expect {
542
- patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
588
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
543
589
  }.to_not change { MockUser.count }
544
590
 
545
591
  expect(response.status).to eql(200)
546
592
  result = JSON.parse(response.body)
547
593
 
548
- expect(result['id']).to eql(@u2.id.to_s)
594
+ expect(result['id']).to eql(@u2.primary_key.to_s)
549
595
  expect(result['meta']['resourceType']).to eql('User')
550
596
 
551
597
  @u2.reload
@@ -571,13 +617,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
571
617
 
572
618
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
573
619
  expect {
574
- patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
620
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
575
621
  }.to_not change { MockUser.count }
576
622
 
577
623
  expect(response.status).to eql(200)
578
624
  result = JSON.parse(response.body)
579
625
 
580
- expect(result['id']).to eql(@u2.id.to_s)
626
+ expect(result['id']).to eql(@u2.primary_key.to_s)
581
627
  expect(result['meta']['resourceType']).to eql('User')
582
628
 
583
629
  @u2.reload
@@ -601,7 +647,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
601
647
 
602
648
  it 'notes Rails validation failures' do
603
649
  expect {
604
- patch "/Users/#{@u2.id}", params: {
650
+ patch "/Users/#{@u2.primary_key}", params: {
605
651
  format: :scim,
606
652
  Operations: [
607
653
  {
@@ -654,7 +700,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
654
700
  expect_any_instance_of(MockUsersController).to receive(:destroy).once.and_call_original
655
701
  expect_any_instance_of(MockUser).to receive(:destroy!).once.and_call_original
656
702
  expect {
657
- delete "/Users/#{@u2.id}", params: { format: :scim }
703
+ delete "/Users/#{@u2.primary_key}", params: { format: :scim }
658
704
  }.to change { MockUser.count }.by(-1)
659
705
 
660
706
  expect(response.status).to eql(204)
@@ -666,7 +712,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
666
712
  expect_any_instance_of(MockUser).to_not receive(:destroy!)
667
713
 
668
714
  expect {
669
- delete "/CustomDestroyUsers/#{@u2.id}", params: { format: :scim }
715
+ delete "/CustomDestroyUsers/#{@u2.primary_key}", params: { format: :scim }
670
716
  }.to_not change { MockUser.count }
671
717
 
672
718
  expect(response.status).to eql(204)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scimitar
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - RIPA Global
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-01-13 00:00:00.000000000 Z
12
+ date: 2023-01-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -211,7 +211,6 @@ files:
211
211
  - spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb
212
212
  - spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb
213
213
  - spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb
214
- - spec/apps/dummy/db/migrate/20230109012729_add_timestamps_to_mock_user.rb
215
214
  - spec/apps/dummy/db/schema.rb
216
215
  - spec/controllers/scimitar/application_controller_spec.rb
217
216
  - spec/controllers/scimitar/resource_types_controller_spec.rb
@@ -261,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
260
  - !ruby/object:Gem::Version
262
261
  version: '0'
263
262
  requirements: []
264
- rubygems_version: 3.4.1
263
+ rubygems_version: 3.4.4
265
264
  signing_key:
266
265
  specification_version: 4
267
266
  summary: SCIM v2 for Rails
@@ -283,7 +282,6 @@ test_files:
283
282
  - spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb
284
283
  - spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb
285
284
  - spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb
286
- - spec/apps/dummy/db/migrate/20230109012729_add_timestamps_to_mock_user.rb
287
285
  - spec/apps/dummy/db/schema.rb
288
286
  - spec/controllers/scimitar/application_controller_spec.rb
289
287
  - spec/controllers/scimitar/resource_types_controller_spec.rb
@@ -1,5 +0,0 @@
1
- class AddTimestampsToMockUser < ActiveRecord::Migration[7.0]
2
- def change
3
- add_timestamps :mock_users
4
- end
5
- end