Skip to content

Commit d2b336a

Browse files
committed
Move set up to a job
OAuth requires the callback to be a GET request, by moving it a Job we can avoid a write from a GET request
1 parent 06ffcc1 commit d2b336a

File tree

6 files changed

+126
-21
lines changed

6 files changed

+126
-21
lines changed

app/controllers/integrations/basecamps/callbacks_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ class Integrations::Basecamps::CallbacksController < ApplicationController
33
allow_unauthenticated_access
44

55
def show
6-
Integration::Basecamp.set_up(code: params[:code], state: params[:state])
6+
Integration::Basecamp.set_up_later(code: params[:code], state: params[:state])
77
end
88
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class Integration::Basecamp::SetUpJob < ApplicationJob
2+
def perform(code:, state:)
3+
Integration::Basecamp.set_up(code: code, state: state)
4+
end
5+
end

app/models/integration/basecamp.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ class Integration::Basecamp < Integration
55
store_accessor :data, :access_token, :refresh_token
66

77
class << self
8+
def set_up_later(code:, state:)
9+
Integration::Basecamp::SetUpJob.perform_later(code: code, state: state)
10+
end
11+
812
def set_up(code:, state:)
913
with_tenant_from_state(state) do
1014
integration = locate_by_state(state)

test/controllers/integrations/basecamps/callbacks_controller_test.rb

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Integrations::Basecamps::CallbacksControllerTest < ActionDispatch::Integra
1010
Rails.application.config.action_controller.default_url_options = @original_url_options
1111
end
1212

13-
test "show sets up integration with code and state" do
13+
test "show enqueues job to set up integration" do
1414
integration = integrations(:kevins_basecamp)
1515
integration.update!(data: {})
1616

@@ -19,24 +19,11 @@ class Integrations::Basecamps::CallbacksControllerTest < ActionDispatch::Integra
1919
for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE
2020
).to_s
2121

22-
credentials = Rails.application.credentials.integrations.basecamp
23-
oauth_server_url = credentials[:oauth_server_url]
24-
25-
stub_request(:post, "#{oauth_server_url}/authorization/token")
26-
.to_return(
27-
status: 200,
28-
body: {
29-
access_token: "new_access_token",
30-
refresh_token: "new_refresh_token"
31-
}.to_json,
32-
headers: { "Content-Type" => "application/json" }
33-
)
34-
35-
get basecamp_integration_callback_path, params: { code: "test_code", state: state }
22+
assert_enqueued_with(job: Integration::Basecamp::SetUpJob, args: [{ code: "test_code", state: state }]) do
23+
get basecamp_integration_callback_path, params: { code: "test_code", state: state }
24+
end
3625

3726
assert_response :success
38-
assert integration.reload.setup?
39-
assert_equal "new_access_token", integration.access_token
40-
assert_equal "new_refresh_token", integration.refresh_token
4127
end
42-
end
28+
end
29+

test/controllers/integrations/basecamps_controller_test.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,23 @@ class Integrations::BasecampsControllerTest < ActionDispatch::IntegrationTest
1717
end
1818

1919
test "create" do
20+
sign_in_as :kevin
21+
22+
assert_no_difference "Integration::Basecamp.count" do
23+
post basecamp_integration_path
24+
end
25+
26+
assert_response :success, "Renders the 'integration already setup' screen"
27+
28+
users(:kevin).integrations.delete_all
29+
2030
assert_difference "Integration::Basecamp.count", 1 do
2131
post basecamp_integration_path
2232
end
2333

2434
integration = Integration::Basecamp.last
2535
assert_equal users(:kevin), integration.owner
26-
assert_response :redirect
36+
assert_response :redirect, "Redirects to launchpad"
2737
assert_match %r{launchpad\.localhost:3011/authorization/new}, response.redirect_url
2838
end
2939
end
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
require "test_helper"
2+
3+
class Integration::BasecampTest < ActiveSupport::TestCase
4+
setup do
5+
@integration = integrations(:kevins_basecamp)
6+
@original_url_options = Rails.application.config.action_controller.default_url_options
7+
Rails.application.config.action_controller.default_url_options = { host: "example.com" }
8+
end
9+
10+
teardown do
11+
Rails.application.config.action_controller.default_url_options = @original_url_options
12+
end
13+
14+
test "setup?" do
15+
assert @integration.setup?
16+
17+
@integration.access_token = nil
18+
@integration.refresh_token = nil
19+
20+
assert_not @integration.setup?
21+
end
22+
23+
test "authorization_url" do
24+
url = @integration.authorization_url
25+
26+
assert_match %r{launchpad\.localhost:3011/authorization/new}, url
27+
assert_match(/client_id=/, url)
28+
assert_match(/redirect_uri=/, url)
29+
assert_match(/state=/, url)
30+
assert_match(/type=web_server/, url)
31+
end
32+
33+
test "refresh_tokens" do
34+
credentials = Rails.application.credentials.integrations.basecamp
35+
36+
stub_request(:post, "#{credentials[:oauth_server_url]}/authorization/token")
37+
.to_return(
38+
status: 200,
39+
body: { access_token: "refreshed_access_token" }.to_json,
40+
headers: { "Content-Type" => "application/json" }
41+
)
42+
43+
@integration.refresh_tokens
44+
45+
assert_equal "refreshed_access_token", @integration.access_token
46+
47+
@integration.update!(data: { access_token: "original_token" })
48+
@integration.refresh_tokens
49+
assert_equal "original_token", @integration.reload.access_token
50+
end
51+
52+
test "set_up_later" do
53+
state = @integration.to_sgid(
54+
expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION,
55+
for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE
56+
).to_s
57+
58+
assert_enqueued_with(job: Integration::Basecamp::SetUpJob, args: [ { code: "code", state: state } ]) do
59+
Integration::Basecamp.set_up_later(code: "code", state: state)
60+
end
61+
end
62+
63+
test "set_up" do
64+
@integration.update!(data: {})
65+
credentials = Rails.application.credentials.integrations.basecamp
66+
67+
state = @integration.to_sgid(
68+
expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION,
69+
for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE
70+
).to_s
71+
72+
stub_request(:post, "#{credentials[:oauth_server_url]}/authorization/token")
73+
.to_return(
74+
status: 200,
75+
body: {
76+
access_token: "new_access_token",
77+
refresh_token: "new_refresh_token"
78+
}.to_json,
79+
headers: { "Content-Type" => "application/json" }
80+
)
81+
82+
Integration::Basecamp.set_up(code: "code", state: state)
83+
84+
assert @integration.reload.setup?
85+
assert_equal "new_access_token", @integration.access_token
86+
assert_equal "new_refresh_token", @integration.refresh_token
87+
88+
state = @integration.to_sgid(
89+
expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION,
90+
for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE
91+
).to_s
92+
93+
original_access_token = @integration.access_token
94+
95+
Integration::Basecamp.set_up(code: "code", state: state)
96+
97+
assert_equal original_access_token, @integration.reload.access_token, "Set up is skipped if the integration already is setup"
98+
end
99+
end

0 commit comments

Comments
 (0)