Skip to content

Commit 5bf1f01

Browse files
authored
Add external_id to user and organization (#381)
* Add external_id to user and organization On create and update (@mxlje already added to user update). * Allow passing of nil for external_id External IDs can actually be set to null. On create this isn't an issue, not sending it will result in it being null. But on update if a user passes nil we want to be sure a null value goes to the API. Since organizations and user update methods are different I opted for a slightly difference solution in each place. * Test that external_id can be set to null
1 parent 579d4fd commit 5bf1f01

File tree

10 files changed

+503
-9
lines changed

10 files changed

+503
-9
lines changed

lib/workos/organization.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class Organization
1212
:domains,
1313
:stripe_customer_id,
1414
:name,
15+
:external_id,
1516
:allow_profiles_outside_organization,
1617
:created_at,
1718
:updated_at,
@@ -22,6 +23,7 @@ def initialize(json)
2223

2324
@id = hash[:id]
2425
@name = hash[:name]
26+
@external_id = hash[:external_id]
2527
@allow_profiles_outside_organization = hash[:allow_profiles_outside_organization]
2628
@domains = hash[:domains]
2729
@stripe_customer_id = hash[:stripe_customer_id]
@@ -33,6 +35,7 @@ def to_json(*)
3335
{
3436
id: id,
3537
name: name,
38+
external_id: external_id,
3639
allow_profiles_outside_organization: allow_profiles_outside_organization,
3740
domains: domains,
3841
stripe_customer_id: stripe_customer_id,

lib/workos/organizations.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def get_organization(id:)
7575
# @option domain_data [String] domain The domain that belongs to the organization.
7676
# @option domain_data [String] state The state of the domain. "verified" or "pending"
7777
# @param [String] name A unique, descriptive name for the organization
78+
# @param [String] external_id The organization's external ID.
7879
# @param [String] idempotency_key An idempotency key
7980
# @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
8081
# within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
@@ -85,11 +86,13 @@ def create_organization(
8586
domain_data: nil,
8687
domains: nil,
8788
name:,
89+
external_id: nil,
8890
allow_profiles_outside_organization: nil,
8991
idempotency_key: nil
9092
)
9193
body = { name: name }
9294
body[:domain_data] = domain_data if domain_data
95+
body[:external_id] = external_id if external_id
9396

9497
if domains
9598
warn_deprecation '`domains` is deprecated. Use `domain_data` instead.'
@@ -123,22 +126,26 @@ def create_organization(
123126
# @option domain_data [String] state The state of the domain. "verified" or "pending"
124127
# @param [String] stripe_customer_id The Stripe customer ID associated with this organization.
125128
# @param [String] name A unique, descriptive name for the organization
129+
# @param [String] external_id The organization's external ID.
126130
# @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
127131
# within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
128132
# Deprecated: If you need to allow sign-ins from any email domain, contact [email protected].
129133
# @param [Array<String>] domains List of domains that belong to the organization.
130134
# Deprecated: Use domain_data instead.
135+
# rubocop:disable Metrics/ParameterLists
131136
def update_organization(
132137
organization:,
133138
stripe_customer_id: nil,
134139
domain_data: nil,
135140
domains: nil,
136141
name: nil,
142+
external_id: :not_set,
137143
allow_profiles_outside_organization: nil
138144
)
139145
body = { name: name }
140146
body[:domain_data] = domain_data if domain_data
141147
body[:stripe_customer_id] = stripe_customer_id if stripe_customer_id
148+
body[:external_id] = external_id if external_id != :not_set
142149

143150
if domains
144151
warn_deprecation '`domains` is deprecated. Use `domain_data` instead.'
@@ -162,6 +169,7 @@ def update_organization(
162169

163170
WorkOS::Organization.new(response.body)
164171
end
172+
# rubocop:enable Metrics/ParameterLists
165173

166174
# Delete an Organization
167175
#

lib/workos/user_management.rb

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def list_users(options = {})
182182
# @param [String] first_name The user's first name.
183183
# @param [String] last_name The user's last name.
184184
# @param [Boolean] email_verified Whether the user's email address was previously verified.
185+
# @param [String] external_id The user's external ID.
185186
# @param [String] password_hash The user's hashed password.
186187
# @option [String] password_hash_type The algorithm originally used to hash the password.
187188
#
@@ -193,6 +194,7 @@ def create_user(
193194
first_name: nil,
194195
last_name: nil,
195196
email_verified: nil,
197+
external_id: nil,
196198
password_hash: nil,
197199
password_hash_type: nil
198200
)
@@ -204,6 +206,7 @@ def create_user(
204206
first_name: first_name,
205207
last_name: last_name,
206208
email_verified: email_verified,
209+
external_id: external_id,
207210
password_hash: password_hash,
208211
password_hash_type: password_hash_type,
209212
}.compact,
@@ -231,14 +234,14 @@ def create_user(
231234
# @return [WorkOS::User]
232235
def update_user(
233236
id:,
234-
email: nil,
235-
first_name: nil,
236-
last_name: nil,
237-
email_verified: nil,
238-
external_id: nil,
239-
password: nil,
240-
password_hash: nil,
241-
password_hash_type: nil
237+
email: :not_set,
238+
first_name: :not_set,
239+
last_name: :not_set,
240+
email_verified: :not_set,
241+
external_id: :not_set,
242+
password: :not_set,
243+
password_hash: :not_set,
244+
password_hash_type: :not_set
242245
)
243246
request = put_request(
244247
path: "/user_management/users/#{id}",
@@ -251,7 +254,7 @@ def update_user(
251254
password: password,
252255
password_hash: password_hash,
253256
password_hash_type: password_hash_type,
254-
}.compact,
257+
}.reject { |_, v| v == :not_set },
255258
auth: true,
256259
)
257260

spec/lib/workos/organizations_spec.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@
3333
end
3434
end
3535

36+
context 'with external_id' do
37+
it 'creates an organization with external_id' do
38+
VCR.use_cassette 'organization/create_with_external_id' do
39+
organization = described_class.create_organization(
40+
name: 'Test Organization with External ID',
41+
external_id: 'ext_org_123',
42+
)
43+
44+
expect(organization.id).to start_with('org_')
45+
expect(organization.name).to eq('Test Organization with External ID')
46+
expect(organization.external_id).to eq('ext_org_123')
47+
end
48+
end
49+
end
50+
3651
context 'with domains' do
3752
it 'creates an organization and warns' do
3853
VCR.use_cassette 'organization/create_with_domains' do
@@ -310,6 +325,43 @@
310325
end
311326
end
312327
end
328+
context 'with an external_id' do
329+
it 'updates the organization' do
330+
VCR.use_cassette 'organization/update_with_external_id' do
331+
organization = described_class.update_organization(
332+
organization: 'org_01K0SQV0S6EPWK2ZDEFD1CP1JC',
333+
name: 'Test Organization',
334+
external_id: 'ext_org_456',
335+
)
336+
337+
expect(organization.id).to eq('org_01K0SQV0S6EPWK2ZDEFD1CP1JC')
338+
expect(organization.name).to eq('Test Organization')
339+
expect(organization.external_id).to eq('ext_org_456')
340+
end
341+
end
342+
end
343+
344+
context 'can set external_id to null explicitly' do
345+
it 'includes external_id null in request body' do
346+
original_method = described_class.method(:put_request)
347+
allow(described_class).to receive(:put_request) do |kwargs|
348+
original_method.call(**kwargs)
349+
end
350+
351+
VCR.use_cassette 'organization/update_with_external_id_null' do
352+
described_class.update_organization(
353+
organization: 'org_01K0SQV0S6EPWK2ZDEFD1CP1JC',
354+
name: 'Test Organization',
355+
external_id: nil,
356+
)
357+
end
358+
359+
# Verify the spy captured the right call
360+
expect(described_class).to have_received(:put_request).with(
361+
hash_including(body: hash_including(external_id: nil)),
362+
)
363+
end
364+
end
313365
end
314366

315367
describe '.delete_organization' do

spec/lib/workos/user_management_spec.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,22 @@
358358
)
359359
end
360360

361+
it 'creates a user with external_id' do
362+
VCR.use_cassette 'user_management/create_user_with_external_id' do
363+
user = described_class.create_user(
364+
365+
first_name: 'External',
366+
last_name: 'User',
367+
external_id: 'ext_user_123',
368+
)
369+
370+
expect(user.first_name).to eq('External')
371+
expect(user.last_name).to eq('User')
372+
expect(user.email).to eq('[email protected]')
373+
expect(user.external_id).to eq('ext_user_123')
374+
end
375+
end
376+
361377
context 'with an invalid payload' do
362378
it 'returns an error' do
363379
VCR.use_cassette 'user_management/create_user_invalid' do
@@ -426,6 +442,25 @@
426442
)
427443
end
428444

445+
it 'can set external_id to null explicitly' do
446+
original_method = described_class.method(:put_request)
447+
allow(described_class).to receive(:put_request) do |kwargs|
448+
original_method.call(**kwargs)
449+
end
450+
451+
VCR.use_cassette 'user_management/update_user_external_id_null' do
452+
described_class.update_user(
453+
id: 'user_01K0SR53HJ58M957MYAB6TDZ9X',
454+
first_name: 'John',
455+
external_id: nil,
456+
)
457+
end
458+
459+
expect(described_class).to have_received(:put_request).with(
460+
hash_including(body: hash_including(external_id: nil)),
461+
)
462+
end
463+
429464
context 'with an invalid payload' do
430465
it 'returns an error' do
431466
VCR.use_cassette 'user_management/update_user/invalid' do

spec/support/fixtures/vcr_cassettes/organization/create_with_external_id.yml

Lines changed: 83 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)