Skip to content

Commit 5205dd8

Browse files
committed
(feature) Allow users to be imported from a CSV
As this is designed to be run from the rails console, `UserImport.new` expects an instance of Auth0Client as its second parameter. It will then create a new Auth0 user for each email address included in the CSV file, skipping any that already exists. Their passwords and uid will be printed to the console.
1 parent 4262548 commit 5205dd8

6 files changed

Lines changed: 172 additions & 0 deletions

File tree

Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,14 @@ gem 'puma', '~> 3.11'
2727
# Audit log
2828
gem 'rails_event_store'
2929

30+
# Auth0
31+
gem 'auth0', require: false
32+
3033
gem 'rubocop'
3134

3235
group :development, :test do
3336
gem 'byebug', platforms: %i[mri mingw x64_mingw]
37+
gem 'dotenv-rails'
3438
gem 'factory_bot_rails'
3539
gem 'pry-rails'
3640
gem 'rspec-rails'

Gemfile.lock

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ GEM
5151
arkency-command_bus (0.4.0)
5252
thread_safe
5353
ast (2.4.0)
54+
auth0 (4.4.0)
55+
rest-client (~> 2.0)
5456
aws-eventstream (1.0.1)
5557
aws-partitions (1.95.0)
5658
aws-sdk-core (3.22.1)
@@ -72,6 +74,12 @@ GEM
7274
crass (1.0.4)
7375
database_cleaner (1.7.0)
7476
diff-lcs (1.3)
77+
domain_name (0.5.20180417)
78+
unf (>= 0.0.5, < 1.0.0)
79+
dotenv (2.4.0)
80+
dotenv-rails (2.4.0)
81+
dotenv (= 2.4.0)
82+
railties (>= 3.2, < 6.0)
7583
erubi (1.7.1)
7684
factory_bot (4.10.0)
7785
activesupport (>= 3.0.0)
@@ -81,6 +89,8 @@ GEM
8189
ffi (1.9.23)
8290
globalid (0.4.1)
8391
activesupport (>= 4.2.0)
92+
http-cookie (1.0.3)
93+
domain_name (~> 0.5)
8494
i18n (1.0.1)
8595
concurrent-ruby (~> 1.0)
8696
jbuilder (2.7.0)
@@ -111,12 +121,16 @@ GEM
111121
marcel (0.3.2)
112122
mimemagic (~> 0.3.2)
113123
method_source (0.9.0)
124+
mime-types (3.1)
125+
mime-types-data (~> 3.2015)
126+
mime-types-data (3.2016.0521)
114127
mimemagic (0.3.2)
115128
mini_mime (1.0.0)
116129
mini_portile2 (2.3.0)
117130
minitest (5.11.3)
118131
msgpack (1.2.4)
119132
multi_json (1.13.1)
133+
netrc (0.11.0)
120134
nio4r (2.3.1)
121135
nokogiri (1.8.2)
122136
mini_portile2 (~> 2.3.0)
@@ -179,6 +193,10 @@ GEM
179193
rb-fsevent (0.10.3)
180194
rb-inotify (0.9.10)
181195
ffi (>= 0.5.0, < 2)
196+
rest-client (2.0.2)
197+
http-cookie (>= 1.0.2, < 2.0)
198+
mime-types (>= 1.16, < 4.0)
199+
netrc (~> 0.8)
182200
rspec (3.7.0)
183201
rspec-core (~> 3.7.0)
184202
rspec-expectations (~> 3.7.0)
@@ -230,6 +248,9 @@ GEM
230248
thread_safe (0.3.6)
231249
tzinfo (1.2.5)
232250
thread_safe (~> 0.1)
251+
unf (0.1.4)
252+
unf_ext
253+
unf_ext (0.0.7.5)
233254
unicode-display_width (1.3.2)
234255
websocket-driver (0.7.0)
235256
websocket-extensions (>= 0.1.0)
@@ -240,10 +261,12 @@ PLATFORMS
240261

241262
DEPENDENCIES
242263
aasm
264+
auth0
243265
aws-sdk-lambda
244266
bootsnap (>= 1.1.0)
245267
byebug
246268
database_cleaner
269+
dotenv-rails
247270
factory_bot_rails
248271
jbuilder (~> 2.5)
249272
jsonapi-rails

lib/user_import.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require 'csv'
2+
require 'auth0'
3+
require 'user_import_row'
4+
5+
class UserImport
6+
def initialize(csv_data, auth0_client)
7+
@rows = CSV.parse(csv_data, headers: true, header_converters: :symbol)
8+
@auth0_client = auth0_client
9+
end
10+
11+
def run!
12+
@rows.each do |row|
13+
UserImportRow.new(row.to_h, @auth0_client).import!
14+
15+
# Needed to stop the Auth0 Management API throttling us
16+
sleep 1
17+
end
18+
end
19+
end

lib/user_import_row.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
class UserImportRow
2+
REQUIRED_KEYS = %i[email personname suppliername].freeze
3+
4+
def initialize(row, auth0_client)
5+
@row = row
6+
@auth0_client = auth0_client
7+
8+
check_for_required_keys!
9+
end
10+
11+
def import!
12+
name = @row[:personname]
13+
email = @row[:email].strip
14+
supplier = @row[:suppliername]
15+
password = SecureRandom.alphanumeric(20)
16+
17+
begin
18+
new_user = @auth0_client.create_user(
19+
name,
20+
connection: 'Username-Password-Authentication',
21+
email: email,
22+
password: password,
23+
verify_email: false,
24+
user_metadata: {
25+
supplier: supplier
26+
}
27+
)
28+
rescue Auth0::Unsupported => e
29+
# Ignore error when user already exists
30+
raise unless JSON.parse(e.message)['statusCode'] == 409
31+
end
32+
33+
Rails.logger.info "#{new_user['user_id']},#{email},#{password},#{name}" if new_user
34+
end
35+
36+
private
37+
38+
def check_for_required_keys!
39+
raise MissingKey unless (REQUIRED_KEYS - @row.keys).empty?
40+
end
41+
42+
class MissingKey < StandardError; end
43+
end

spec/lib/user_import_row_spec.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
require 'rails_helper'
2+
require './lib/user_import_row'
3+
4+
RSpec.describe UserImportRow do
5+
describe '#initialize' do
6+
it 'throws an error if the hash is missing required fields' do
7+
auth0_client = instance_double('auth0 client')
8+
9+
data = {
10+
personname: 'Daffy Duck'
11+
}
12+
13+
expect { UserImportRow.new(data, auth0_client) }
14+
.to raise_error('UserImportRow::MissingKey')
15+
end
16+
end
17+
18+
describe '#import!' do
19+
it 'creates a new user on Auth0' do
20+
data = {
21+
frameworkreference: 'RM3756',
22+
supplierid: 1234,
23+
suppliername: 'Bob and Bucket LLP',
24+
lotid: 5678,
25+
lotnumber: 1,
26+
username: 'francis.brown',
27+
personname: 'Francis Brown',
28+
jobtitle: 'Big Cheese',
29+
contacttel: '02088118055',
30+
contactmobile: '07900111222',
31+
email: 'francis.brown@example.com',
32+
usercreationdate: '1/1/1970 18:00',
33+
lastlogindate: '1/7/2018 12:40'
34+
}
35+
36+
auth0_client = instance_double('auth0 client')
37+
38+
expect(auth0_client)
39+
.to receive(:create_user)
40+
.with(
41+
'Francis Brown',
42+
a_hash_including(
43+
email: 'francis.brown@example.com'
44+
)
45+
)
46+
47+
UserImportRow.new(data, auth0_client).import!
48+
end
49+
end
50+
end

spec/lib/user_import_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require 'rails_helper'
2+
3+
require './lib/user_import'
4+
5+
RSpec.describe UserImport do
6+
describe '#initialize' do
7+
it 'accepts CSV data and an Auth0 client as input' do
8+
client = instance_double('auth0 client')
9+
10+
csv = "suppliername,email,personname\n"
11+
csv += "Vaughan Legal,email@example.com,Jo Bloggs\n"
12+
csv += "Vaughan Legal,email2@example.com,Francis Bloggs\n"
13+
14+
expect { UserImport.new(csv, client) }.to_not raise_error
15+
end
16+
end
17+
18+
describe '#run!' do
19+
it 'calls UserImportRow.import! for each row contained in the CSV' do
20+
client = instance_double('auth0 client')
21+
allow(client).to receive(:create_user).and_return('user_id' => 'auth0|user_id')
22+
23+
csv = "suppliername,email,personname\n"
24+
csv += "Vaughan Legal,email@example.com,Jo Bloggs\n"
25+
csv += "Vaughan Legal,email2@example.com,Francis Bloggs\n"
26+
csv += "Vaughan Legal,email3@example.com,Alex Bloggs\n"
27+
28+
UserImport.new(csv, client).run!
29+
30+
expect(client).to have_received(:create_user).exactly(3).times
31+
end
32+
end
33+
end

0 commit comments

Comments
 (0)