Skip to content

Commit 93293a0

Browse files
authored
Merge pull request #106 from Jess-White/feature/412-add-invitations-reinvite-endpoint
Implement invitations reinvite endpoint
2 parents ef04dde + c079233 commit 93293a0

File tree

6 files changed

+142
-49
lines changed

6 files changed

+142
-49
lines changed

app/controllers/api/invitations_controller.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ def index
1010
end
1111

1212
def create
13-
invitation_creator = InvitationCreator.new(create_invitation_params, @organization)
14-
@invitation = invitation_creator.call!
13+
invitation_issuer = InvitationIssuer.new(create_invitation_params, @organization)
14+
@invitation = invitation_issuer.call!
1515
render 'show.json.jb', status: :created
1616
end
1717

@@ -26,6 +26,14 @@ def accept
2626
}
2727
end
2828

29+
def reinvite
30+
invitation_email = Invitation.find(params[:id]).email
31+
invitation_issuer = InvitationIssuer.new({ email: invitation_email }, @organization)
32+
@invitation = invitation_issuer.call!
33+
34+
render 'show.json.jb'
35+
end
36+
2937
private
3038

3139
def create_invitation_params

app/services/invitation_creator.rb renamed to app/services/invitation_issuer.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# frozen_string_literal: true
22

3-
class InvitationCreator
3+
# Service for issuing a new invitiation or re-inviting an existing invitation if
4+
# it has expired.
5+
class InvitationIssuer
46
def initialize(invitation_params, organization)
57
@invitation_params = invitation_params
68
@organization = organization
@@ -12,7 +14,7 @@ def call!
1214

1315
InvitationMailer.with(invitation: invitation).invite.deliver_later
1416

15-
Rails.logger.info("New invitation #{invitation} created")
17+
Rails.logger.info("Invitation #{invitation} issued")
1618

1719
invitation
1820
end
@@ -25,10 +27,12 @@ def find_or_create_invitation!
2527
invitation = Invitation.find_by(email: invitation_params[:email], organization_id: organization.id)
2628
return invitation if invitation.present?
2729

28-
Invitation.create!(
30+
invitation = Invitation.create!(
2931
**invitation_params,
3032
organization: organization
3133
)
34+
Rails.logger.info("New invitation #{invitation} created")
35+
invitation
3236
end
3337

3438
def generate_invitation_token!(invitation)

config/routes.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
resources :organizations do
1818
resources :organization_users, path: :users
1919
resources :boilerplates
20-
resources :invitations
20+
resources :invitations do
21+
post :reinvite, on: :member
22+
end
2123
resources :categories
2224
resources :funding_orgs
2325
resources :grants do

spec/controllers/api/invitations_controller_spec.rb

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,49 @@
161161
end
162162

163163
describe 'PATCH /organization/:organization_id/invitations/:id/reinvite' do
164-
pending "add some examples (or delete) #{__FILE__}"
164+
let!(:invitation) { create(:invitation, organization: organization, expires_at: Date.current) }
165+
166+
context 'when organization does not exist' do
167+
it 'renders 401' do
168+
post :reinvite, params: {
169+
organization_id: 123,
170+
id: invitation.id
171+
}
172+
173+
expect(response).to have_http_status(401)
174+
end
175+
end
176+
177+
context 'when invitation does not exist' do
178+
it 'renders 401' do
179+
post :reinvite, params: {
180+
organization_id: invitation.organization.id,
181+
id: 123
182+
}
183+
184+
expect(response).to have_http_status(401)
185+
end
186+
end
187+
188+
context 'when given valid invitation' do
189+
it 'renders 200' do
190+
post :reinvite, params: {
191+
organization_id: invitation.organization.id,
192+
id: invitation.id
193+
}
194+
195+
expect(response).to have_http_status(200)
196+
expect(JSON.parse(response.body).keys).to contain_exactly(*invitation_fields)
197+
expect(JSON.parse(response.body)).to match(
198+
a_hash_including(
199+
'id' => invitation.id,
200+
# Calling reload here since the invitation issuer will update these fields
201+
'updated_at' => invitation.reload.updated_at.iso8601(3),
202+
'expires_at' => invitation.reload.expires_at.iso8601
203+
)
204+
)
205+
end
206+
end
165207
end
166208

167209
describe 'DELETE /organization/:organization_id/invitations/:id' do

spec/services/invitation_creator_spec.rb

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
describe InvitationIssuer do
6+
let(:organization) { create(:organization) }
7+
let(:invitation_params) { { first_name: 'Elenor', last_name: 'Shellstrop', email: '[email protected]' } }
8+
9+
before do
10+
ActiveJob::Base.queue_adapter = :test
11+
end
12+
13+
describe '#call!' do
14+
context 'when invitation does not exist' do
15+
subject { InvitationIssuer.new(invitation_params, organization) }
16+
let!(:result) { subject.call! }
17+
18+
it 'returns created invitation' do
19+
expect(result).to be_instance_of(Invitation)
20+
expect(result.first_name).to eq('Elenor')
21+
end
22+
23+
it 'creates an invitation using params' do
24+
expect(Invitation.last.first_name).to eq('Elenor')
25+
end
26+
27+
it 'adds invitation to provided organization' do
28+
expect(organization.invitations).to include(result)
29+
end
30+
31+
it 'generate a token for the invitation' do
32+
expect(Invitation.last.token).to be_instance_of(String)
33+
end
34+
35+
it 'sets expiration date of token to a week from now' do
36+
expect(Invitation.last.expires_at).to eq(1.week.from_now.to_date)
37+
end
38+
39+
it 'schedules email to user' do
40+
expect { subject.call! }.to have_enqueued_mail(InvitationMailer, :invite)
41+
end
42+
end
43+
44+
context 'when invitation already exists' do
45+
let!(:existing_invitation) do
46+
create(:invitation, organization: organization, email: invitation_params[:email], expires_at: Date.current)
47+
end
48+
49+
subject { InvitationIssuer.new(invitation_params, organization) }
50+
let!(:result) { subject.call! }
51+
52+
it 'returns existing invitation' do
53+
expect(result).to eq(existing_invitation)
54+
end
55+
56+
it 'does not create duplicate invitation' do
57+
expect(Invitation.count).to eq(1)
58+
end
59+
60+
it 'does not create duplicate invitation association within organization' do
61+
expect(organization.invitations).to match_array([result])
62+
end
63+
64+
it 'generate a new token to re-issue the invitation' do
65+
expect(result.token).to be_instance_of(String)
66+
expect(result.token).not_to eq(existing_invitation.token)
67+
end
68+
69+
it 'sets expiration date of new token to a week from now' do
70+
expect(result.expires_at).not_to eq(existing_invitation.expires_at)
71+
expect(result.expires_at).to eq(1.week.from_now.to_date)
72+
end
73+
74+
it 'schedules email to user' do
75+
expect { subject.call! }.to have_enqueued_mail(InvitationMailer, :invite)
76+
end
77+
end
78+
end
79+
end

0 commit comments

Comments
 (0)