Skip to content

Commit 9ae69d7

Browse files
authored
Merge pull request #15 from dxw/feature/142-create-supplier-user-accounts-in-auth0
(142) Import suppliers / Create users in Auth0
2 parents aa1c584 + aabb860 commit 9ae69d7

14 files changed

Lines changed: 370 additions & 0 deletions

.rubocop.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,8 @@ Metrics/BlockLength:
6565
Max: 40
6666
Exclude:
6767
- 'spec/**/*'
68+
69+
Lint/AmbiguousBlockAssociation:
70+
Exclude:
71+
- 'spec/**/*'
72+

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

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ Rebuilds the test server, runs rubocop checks, runs the test suite and cleans up
4343
## API Endpoints
4444

4545
A [full list of the API endpoints](endpoints.md) is available in a separate document.
46+
47+
## Importing users
48+
49+
See [this guide](importing.md) to see how to import users and suppliers.

importing.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Import Users / Suppliers
2+
3+
Users and suppliers are imported from a CSV which MUST have the following
4+
fields:
5+
6+
- personname (full name of user)
7+
- email
8+
- suppliername
9+
- frameworkreference (the framework's RM number)
10+
11+
Also, the file needs to encoded in UTF-8 or you'll get errors! To convert an
12+
existing file, run the following on the command line:
13+
14+
`iconv -c -f "US-ASCII" -t "UTF-8" input.csv > output.csv`
15+
16+
The following commands can be run from the Rails console, given a `csv`
17+
variable that contains the contents of a CSV as a string.
18+
19+
# Importing suppliers
20+
21+
Running `require 'supplier_import'; SupplierImport.new(csv).run!` will:
22+
23+
- Create any suppliers that don't already exist in the database
24+
- Assign these suppliers to the correct frameworks (NB: these should already
25+
exist)
26+
27+
# Importing users
28+
29+
First, you'll need to obtain the Auth0 client_id, token and domain. Then you'll
30+
need to set the `csv` variable as above.
31+
32+
Once you have these, run the following from the Rails console.
33+
34+
```ruby
35+
client = Auth0Client.new(client_id: client_id, token: token, domain: domain)
36+
UserImport.new(csv, client).run!
37+
```
38+
39+
You'll see an output like this (one for every row in the CSV)
40+
41+
```
42+
auth0:1234566788,email@example.com,password,John Smith
43+
```
44+
45+
**Make a note of this, as it's the only place where you can see the passwords
46+
generated for each user.**
47+
48+

lib/supplier_import.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require 'csv'
2+
require 'supplier_import_row'
3+
4+
class SupplierImport
5+
def initialize(csv_data)
6+
@rows = CSV.parse(csv_data, headers: true, header_converters: :symbol)
7+
end
8+
9+
def run!
10+
@rows.each do |row|
11+
SupplierImportRow.new(row.to_h).import!
12+
end
13+
end
14+
end

lib/supplier_import_row.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class SupplierImportRow
2+
REQUIRED_KEYS = %i[frameworkreference suppliername].freeze
3+
4+
def initialize(row)
5+
@row = row
6+
7+
check_for_required_keys!
8+
end
9+
10+
def import!
11+
supplier = Supplier.find_or_create_by!(name: @row[:suppliername])
12+
framework = Framework.find_by!(short_name: @row[:frameworkreference])
13+
14+
supplier.agreements.find_or_create_by!(framework: framework)
15+
end
16+
17+
private
18+
19+
def check_for_required_keys!
20+
raise MissingKey unless (REQUIRED_KEYS - @row.keys).empty?
21+
end
22+
23+
class MissingKey < StandardError; end
24+
end

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/factories/agreement.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FactoryBot.define do
2+
factory :agreement do
3+
supplier
4+
framework
5+
end
6+
end

0 commit comments

Comments
 (0)