disco_app 0.12.1 → 0.12.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/components/shopify/buttons/_buttons.scss +1 -0
- data/app/clients/disco_app/api_client.rb +27 -0
- data/app/clients/disco_app/disco_api_error.rb +2 -0
- data/app/controllers/disco_app/admin/application_controller.rb +7 -0
- data/app/controllers/disco_app/concerns/authenticated_controller.rb +15 -0
- data/app/controllers/sessions_controller.rb +6 -1
- data/app/jobs/disco_app/concerns/app_uninstalled_job.rb +1 -0
- data/app/jobs/disco_app/concerns/subscription_changed_job.rb +1 -0
- data/app/jobs/disco_app/send_subscription_job.rb +7 -0
- data/app/models/disco_app/concerns/shop.rb +10 -0
- data/app/models/disco_app/concerns/subscription.rb +6 -0
- data/app/services/disco_app/request_validation_service.rb +15 -0
- data/app/views/shopify_app/sessions/new.html.erb +42 -0
- data/config/routes.rb +1 -0
- data/lib/disco_app/version.rb +1 -1
- data/lib/generators/disco_app/templates/initializers/rollbar.rb +4 -0
- data/lib/generators/disco_app/templates/initializers/shopify_app.rb +0 -1
- data/lib/tasks/api.rake +10 -0
- data/test/clients/disco_app/api_client_test.rb +22 -0
- data/test/controllers/home_controller_test.rb +10 -1
- data/test/dummy/app/controllers/application_controller.rb +1 -1
- data/test/dummy/config/initializers/omniauth.rb +0 -2
- data/test/dummy/config/initializers/shopify_app.rb +0 -1
- data/test/fixtures/api/subscriptions/valid_request.json +40 -0
- data/test/fixtures/disco_app/recurring_application_charges.yml +2 -0
- data/test/fixtures/disco_app/shops.yml +2 -0
- data/test/fixtures/disco_app/subscriptions.yml +3 -0
- data/test/jobs/disco_app/app_installed_job_test.rb +1 -1
- data/test/jobs/disco_app/app_uninstalled_job_test.rb +4 -4
- data/test/jobs/disco_app/send_subscription_job_test.rb +24 -0
- data/test/test_helper.rb +1 -0
- metadata +21 -4
- data/app/views/sessions/new.html.erb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca27fe14e53f4e34f76940c75b601e8534f3b0fc30a2fc668951793e0ab05846
|
4
|
+
data.tar.gz: def89441dbebd476d9efac4889b6854ff2e5e32d7192154b391b4d8144da26ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bce10166c303ac988305dee9c40acb2d7ce05eff63c368144f3941784e7ef51e970c42b8350702b0e3075f65e8ed3a56ef99921d8705edea4b00c263e0cac2e
|
7
|
+
data.tar.gz: 73055ae6330d43924b75aaad82044afe09ecffb91964b7a13b2e3b2d9b748619af3a541adf0877d1820d93bed717416b62312d003219f6b2526eb9ef3dabb41d
|
@@ -226,6 +226,7 @@
|
|
226
226
|
cursor: default;
|
227
227
|
color: transparent;
|
228
228
|
text-shadow: none;
|
229
|
+
background: #fff url(data:image/gif;base64,R0lGODlhEAAQAPUAAC8vL3Z2dpSUlKWlpa2trbW1tb29vcbGxs7OztbW1t7e3ufn5%2B%2Fv7%2Ff39%2F%2F%2F%2F%2F%2F%2F%2Fz09PXNzc4uLizk5OX9%2Ffzo6On19fZiYmEFBQW9vbzw8PEBAQGtrazU1NWdnZ4GBgSUlJWRkZJCQkD4%2BPj8%2FPy4uLo2NjQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwH%2F%2FwAh%2BQQJBgAPACwAAAAAEAAQAAAEifDJ52QJdeq3QnoC4DSFsnFAAAIPMizaAheAEbrI0yyOEwSGRkCAKAwIO2PjoQikGktK4zAowCauzCTxkRgGYNNmajBQBWixhlEoEAyTBUIrUVwlUwFy8z0wKEYIUwkNDApBCUdLdhwDCUULjg8MCVokBQ4HBA9UfxsLBDCaIwRdGiMSCAYVDVoRACH5BAkGAAAALAAAAAAQABAAAAWRICACjjhE5agCS6QAEeQ0kLECTBTBECBBB9WCATgAYwnIrrFwOCQSRCMyQBgGEMaiMGiwTpKGF9BgcAsLVcKQGiUSI2sBfWscDIY7gUBfmQsENiIMCm0iCmkjDQgDBGMjVwdEDlxSBwllCgYNCY1eiF8JCGgDcAwJbQ1zDgcERQNEKwsEaa0zBHArMyJWJQ1tIQAh%2BQQJBgAAACwAAAAAEAAQAAAFkSAgAo5ICOWoAouwABTlMJSxAvQACNQ%2BHaoFA4CgJHiJSa%2BhcDgKg0RDYFAgCBNGMoszDAqNhsjRoEwmhRQLoRZRBCOEwXAYrlrzg4FAKLxWDAUFBDYiDE0rCn8iDQgDBGIqXnUkUAgNB1KHBg0Jj2KKLFEIflE4CWoNgg4HBAAHA3ZBBC%2BtZAQJN2QiciUNaiEAIfkECQYAAAAsAAAAABAAEAAABZMgIAKOaAzlqAJMsQCD5TQWsgINUcAWXNmjBQOgGCQIF0XlglM4HIWBomE4LBIGC%2BNQqTRYp0IjNbN0DSnAApEWXQYjhKE6XC0slwvBQNC9VkpdFSMMTit7Kg0IAwRfKicHQ1ADCA0HCQ2FBg0JjF8KLwtGCC5GLAlpDQUFDgcEAAcDdSoLBC%2BuMwQJNzMiciVjIyEAIfkECQYAAAAsAAAAABAAEAAABZEgIAKOiBjlqAJMwQAH4TRDsgINYcADUGQK1eKlGCgOg0WmgFM4HAVjw4BgKBASBiKTabAMg0IjNZNgMogUYJFeEZgmg%2BHwWi0kXImB8F7cFBkYZyNWaiIFByoNCAMEXiMOggMvUAMIDQcJDQwJGXeCXgp%2BCzUIBaQ7CQNqDQUFDjE8dUIEfjEzBDYrM3ElYyMhACH5BAkGAAAALAAAAAAQABAAAAWSICACjogY5agCTMEAB%2BE0hbICDWHABIAMC9XipRgoYowBArdwOArGhgHBWCgIjMSF0mAZBoVGakagXBJCRGp0OIxOhsNrxdiaDQRCIbhaUP4UIwwKayIFbiMNPwRdIw4UGhQ2T0oNBwkNCwQaCwMaGl0KQZ0JCBcFGgUACQVrNK0xAJ98QgRBMQ4LqTczJigsayEAIfkECQYAAAAsAAAAABAAEAAABZIgIAKOiBjlqAJMwQAH4TSFsgINYcAEgAwL1eKlGChijAECt3A4CsaGAcFYKAiNBbTBMgwKjdTsMCDYRgtEanRYmgyGw2vFIAy%2BBgKhEKQPJBIDIwwKayJtKg0%2FWCoOEhsSQU9KDWQKCQUcWhwcXApBC2WcBxuCR2s0BQ4cGwCsCTcLBEEbGw4LGzsrMyKcJWEjIQAh%2BQQJBgAAACwAAAAAEAAQAAAFkyAgAo6IGOWoAkzBAAfhNIWyAg1hwASADAvV4qUYKGKMAQK3cDgKxoYBwVgoCI0FtMEyDAqN1OzwDY4WiNQokRidDIfXqnEwwA0EQsGsYuR1IwwJaiZtIw0%2FHlwjDgMfAy9PXwoeQEcfDAcfH1wKQQYdBR8eCB49aGoMHR8OHh0AozYrBx02rg4MHgc3MyKjJWEjIQAh%2BQQJBgAAACwAAAAAEAAQAAAGmUCAEOAQIgzFoRLAKDAAB4KjUVAsAQ2CAUoAIAYL5eKpGCiijAECu3A4CuaGAcFYKAiNBbzBNAwKDUlTB39hQwsISUMJCUNHBgdPSw0HBpAGBAQDjUtNBVqOA4pCCoZCDCEgIHxKfpFEqiILIbILCSIMCQN4AFVQIAQiIQkhB0wJiqghDsIAwaaLIFvCDgwia0tTQgMiRYFDQQAh%2BQQJBgAAACwAAAAAEAAQAAAGmECAEOAQIgzFoRLAKDAAB4KjUVAsAQ2CAUoAIAYL5eKpGCiijAECu3A4CuaGAcFYKAiNBbzBNAwKDUlTB39hQwciSUMJCUMZIyMZa0sNBwYGiBmaB1dNBVqLBYpCCoZCDCKQfEp%2BB08OjyILIgQMDAkGDQkDeFCcCSOfImWNt4oNmg4DIgB%2BT0sLkgDLDgwEjUtTQn5FgUNBACH5BAkGAAAALAAAAAAQABAAAAaVQIAQ4BAiDMWhEsAoMAAHgqNRUCwBDYIBSgAgBgvlwfpVRBkDBHbhcERIA0bkwFgoCI1FYdAALEQkJAx9RA0HAwVhQwciSUMJCUMiEZSRS4YGBgdvgQdXTQVaQwpISwqKQg0DJBGEQwYDdERvcVoMDKQNCQN4XmoKcwaJA5EMCY4NIo0GXYdPS3%2BRzFMElkpTRqUNjkEAIfkECQYAAAAsAAAAABAAEAAABptAgBDgECIMxaESoCghAAeCo1FQLAGMUglKACAGC2XCeigRzozBs7FwODLmRiaTWCgI7MKgAUjAMw18RA0HAwVhQwcmSUMJCUMDJpJWS4QGBopzdFcMBQUEBkMLCIxCCohCDQaSgkMGAwcMRAMZBQwHCQ0MCgYNCQN4Xo8LAwkIh8VYCYxUBQ5RUAOySwsEYVFTBI9LU0ZIAA2MQQA7) 50% 50% no-repeat !important;
|
229
230
|
}
|
230
231
|
|
231
232
|
.btn:not(.btn-disabled).is-loading::before, .btn:not(.btn-disabled).is-loading:hover::before {
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
|
3
|
+
class DiscoApp::ApiClient
|
4
|
+
|
5
|
+
SUBSCRIPTION_ENDPOINT = 'app_subscriptions.json'
|
6
|
+
|
7
|
+
def initialize(shop, url)
|
8
|
+
@shop = shop
|
9
|
+
@url = url
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_app_subscription
|
13
|
+
return unless @url.present?
|
14
|
+
url = @url + SUBSCRIPTION_ENDPOINT
|
15
|
+
begin
|
16
|
+
response = RestClient::Request.execute(
|
17
|
+
method: :post,
|
18
|
+
headers: { content_type: :json },
|
19
|
+
url: url,
|
20
|
+
payload: { shop: @shop, subscription: @shop.current_subscription }.to_json
|
21
|
+
)
|
22
|
+
rescue RestClient::BadRequest, RestClient::ResourceNotFound => e
|
23
|
+
raise DiscoApiError.new(e.message)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module DiscoApp::Concerns::AuthenticatedController
|
2
2
|
extend ActiveSupport::Concern
|
3
|
+
include ShopifyApp::LoginProtection
|
3
4
|
|
4
5
|
included do
|
6
|
+
before_action :auto_login
|
5
7
|
before_action :login_again_if_different_shop
|
6
8
|
before_action :shopify_shop
|
7
9
|
before_action :check_installed
|
@@ -13,6 +15,15 @@ module DiscoApp::Concerns::AuthenticatedController
|
|
13
15
|
|
14
16
|
private
|
15
17
|
|
18
|
+
def auto_login
|
19
|
+
if shop_session.nil? and request_hmac_valid?
|
20
|
+
if(shop = DiscoApp::Shop.find_by_shopify_domain(sanitized_shop_name)).present?
|
21
|
+
session[:shopify] = shop.id
|
22
|
+
session[:shopify_domain] = sanitized_shop_name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
16
27
|
def shopify_shop
|
17
28
|
if shop_session
|
18
29
|
@shop = DiscoApp::Shop.find_by!(shopify_domain: @shop_session.url)
|
@@ -53,4 +64,8 @@ module DiscoApp::Concerns::AuthenticatedController
|
|
53
64
|
end
|
54
65
|
end
|
55
66
|
|
67
|
+
def request_hmac_valid?
|
68
|
+
DiscoApp::RequestValidationService.hmac_valid?(request.query_string, ShopifyApp.configuration.secret)
|
69
|
+
end
|
70
|
+
|
56
71
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class SessionsController < ApplicationController
|
2
|
-
include ShopifyApp::
|
2
|
+
include ShopifyApp::SessionsConcern
|
3
3
|
|
4
4
|
def referral
|
5
5
|
cookies[DiscoApp::SOURCE_COOKIE_KEY] = params[:source] if params[:source].present?
|
@@ -7,6 +7,11 @@ class SessionsController < ApplicationController
|
|
7
7
|
redirect_to root_path
|
8
8
|
end
|
9
9
|
|
10
|
+
def failure
|
11
|
+
flash[:notice] = 'There was an issue while trying to authenticate, please retry'
|
12
|
+
redirect_to root_path
|
13
|
+
end
|
14
|
+
|
10
15
|
protected
|
11
16
|
|
12
17
|
# Override the authenticate method to allow skipping OAuth in development
|
@@ -61,6 +61,11 @@ module DiscoApp::Concerns::Shop
|
|
61
61
|
"https://#{shopify_domain}/admin"
|
62
62
|
end
|
63
63
|
|
64
|
+
# Convenience method to get the email of the shop's admin, to display in Rollbar.
|
65
|
+
def email
|
66
|
+
self.data['email']
|
67
|
+
end
|
68
|
+
|
64
69
|
def installed_duration
|
65
70
|
distance_of_time_in_words_to_now(created_at.time)
|
66
71
|
end
|
@@ -81,6 +86,11 @@ module DiscoApp::Concerns::Shop
|
|
81
86
|
(data['primary_locale'] || 'en').to_sym
|
82
87
|
end
|
83
88
|
|
89
|
+
# Return an instance of the Disco API client.
|
90
|
+
def disco_api_client
|
91
|
+
@api_client ||= DiscoApp::ApiClient.new(self, ENV['DISCO_API_URL'])
|
92
|
+
end
|
93
|
+
|
84
94
|
end
|
85
95
|
|
86
96
|
end
|
@@ -47,6 +47,12 @@ module DiscoApp::Concerns::Subscription
|
|
47
47
|
recurring? ? ShopifyAPI::RecurringApplicationCharge : ShopifyAPI::ApplicationCharge
|
48
48
|
end
|
49
49
|
|
50
|
+
def as_json(options = {})
|
51
|
+
super.merge(
|
52
|
+
'active_charge' => active_charge
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
50
56
|
private
|
51
57
|
|
52
58
|
# If the amount or trial period for this subscription changes, clear any
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class DiscoApp::RequestValidationService
|
2
|
+
|
3
|
+
def self.hmac_valid?(query_string, secret)
|
4
|
+
query_hash = Rack::Utils.parse_query(query_string)
|
5
|
+
hmac = query_hash.delete('hmac').to_s
|
6
|
+
ActiveSupport::SecurityUtils.variable_size_secure_compare(self.calculated_hmac(query_hash, secret), hmac)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Return the calculated hmac for the given query hash and secret.
|
10
|
+
def self.calculated_hmac(query_hash, secret)
|
11
|
+
sorted_params = query_hash.collect{ |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join('&')
|
12
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, sorted_params)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Install <%= DiscoApp.configuration.app_name %></title>
|
5
|
+
|
6
|
+
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
|
7
|
+
|
8
|
+
<%= csrf_meta_tags %>
|
9
|
+
|
10
|
+
<%= yield :extra_head %>
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
<%= form_tag shopify_app.login_path do %>
|
14
|
+
<div class="ui-empty-state">
|
15
|
+
<section class="ui-empty-state__section">
|
16
|
+
<div class="ui-empty-state__subsection">
|
17
|
+
<h1 class="ui-empty-state__title">Install <%= DiscoApp.configuration.app_name %></h1>
|
18
|
+
<h2 class="ui-empty-state__subtitle">Enter your Shopify store name below to get started.</h2>
|
19
|
+
</div>
|
20
|
+
<div class="ui-empty-state__subsection">
|
21
|
+
<div class="next-grid">
|
22
|
+
<div class="next-grid__cell" style="text-align: right;">
|
23
|
+
<label for="shop" class="next-input__help-text">https://</label>
|
24
|
+
</div>
|
25
|
+
<div class="next-grid__cell">
|
26
|
+
<input type="text" class="form-control" id="shop" name="shop" placeholder="your-store" autocomplete="off" autofocus="on" />
|
27
|
+
</div>
|
28
|
+
<div class="next-grid__cell" style="text-align: left;">
|
29
|
+
<label for="shop" class="next-input__help-text">.myshopify.com</label>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
<div class="ui-empty-state__subsection">
|
34
|
+
<button type="submit" class="btn btn-large btn-primary">Install</button>
|
35
|
+
</div>
|
36
|
+
</section>
|
37
|
+
</div>
|
38
|
+
<% end %>
|
39
|
+
|
40
|
+
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
|
41
|
+
</body>
|
42
|
+
</html>
|
data/config/routes.rb
CHANGED
data/lib/disco_app/version.rb
CHANGED
@@ -10,6 +10,10 @@ Rollbar.configure do |config|
|
|
10
10
|
# Enable delayed reporting (using Sidekiq)
|
11
11
|
config.use_sidekiq
|
12
12
|
|
13
|
+
# Enable "Person" feature of Rollbar in the context of a "Shop"
|
14
|
+
config.person_method = 'current_shop'
|
15
|
+
config.person_username_method = 'shopify_domain'
|
16
|
+
|
13
17
|
# Add custom handlers.
|
14
18
|
config.before_process << proc do |options|
|
15
19
|
if options[:exception].is_a?(ActiveResource::ClientError) and options[:exception].message.include?('Too Many Requests')
|
data/lib/tasks/api.rake
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ApiClientTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@shop = disco_app_shops(:widget_store)
|
7
|
+
stub_request(:post, "https://api.discolabs.com/v1/app_subscriptions.json").
|
8
|
+
with(body: api_fixture('subscriptions/valid_request').to_json).
|
9
|
+
to_return(status: 200, body: api_fixture('subscriptions/valid_request').to_json)
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
@shop = nil
|
14
|
+
WebMock.reset!
|
15
|
+
end
|
16
|
+
|
17
|
+
test 'Successful disco api call render correct JSON' do
|
18
|
+
response = @shop.disco_api_client.create_app_subscription
|
19
|
+
assert_equal api_fixture('subscriptions/valid_request'), JSON.parse(response.body)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -14,7 +14,7 @@ class HomeControllerTest < ActionController::TestCase
|
|
14
14
|
@current_subscription = nil
|
15
15
|
end
|
16
16
|
|
17
|
-
test 'non-logged in user is redirected to the login page' do
|
17
|
+
test 'non-logged in user is redirected to the login page if no hmac and shop domain present' do
|
18
18
|
log_out
|
19
19
|
get(:index)
|
20
20
|
assert_redirected_to ShopifyApp::Engine.routes.url_helpers.login_path
|
@@ -89,4 +89,13 @@ class HomeControllerTest < ActionController::TestCase
|
|
89
89
|
assert_response :success
|
90
90
|
end
|
91
91
|
|
92
|
+
test 'non-logged in user is logged in if valid hmac and shop domain is present in url' do
|
93
|
+
log_out
|
94
|
+
Timecop.freeze('2017-03-08 12:44:58 +1100') do
|
95
|
+
hmac = 'eb49ba93a8daf8a11a04c66129faf98de1cd40f082b0ae78e79a2dfbbefb438d'
|
96
|
+
get(:index, { hmac: hmac, shop: 'widgets-dev.myshopify.com', timestamp: Time.now.to_i })
|
97
|
+
assert_response :success
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
92
101
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class ApplicationController < ActionController::Base
|
2
|
-
include ShopifyApp::
|
2
|
+
include ShopifyApp::LoginProtection
|
3
3
|
# Prevent CSRF attacks by raising an exception.
|
4
4
|
# For APIs, you may want to use :null_session instead.
|
5
5
|
protect_from_forgery with: :exception
|
@@ -0,0 +1,40 @@
|
|
1
|
+
{
|
2
|
+
"shop": {
|
3
|
+
"id": 605094243,
|
4
|
+
"shopify_domain": "widgets.myshopify.com",
|
5
|
+
"shopify_token": "0",
|
6
|
+
"created_at": "2017-03-07T06:06:25.000Z",
|
7
|
+
"updated_at": "2017-03-07T06:06:25.000Z",
|
8
|
+
"status": "never_installed",
|
9
|
+
"domain": null,
|
10
|
+
"plan_name": null,
|
11
|
+
"name": null,
|
12
|
+
"data": {"timezone": "(GMT+10:00) Melbourne", "country_name": "Australia"}
|
13
|
+
},
|
14
|
+
"subscription": {
|
15
|
+
"id": 304261385,
|
16
|
+
"shop_id": 605094243,
|
17
|
+
"plan_id": 276395349,
|
18
|
+
"status": "active",
|
19
|
+
"subscription_type": "recurring",
|
20
|
+
"created_at": "2017-03-07T06:06:25.000Z",
|
21
|
+
"updated_at": "2017-03-07T06:06:25.000Z",
|
22
|
+
"trial_start_at": null,
|
23
|
+
"trial_end_at": null,
|
24
|
+
"cancelled_at": null,
|
25
|
+
"amount": 999,
|
26
|
+
"plan_code_id": null,
|
27
|
+
"source": null,
|
28
|
+
"trial_period_days": 14,
|
29
|
+
"active_charge": {
|
30
|
+
"id":332186283,
|
31
|
+
"shop_id":605094243,
|
32
|
+
"subscription_id":304261385,
|
33
|
+
"status":"active",
|
34
|
+
"created_at":"2017-03-07T06:06:25.000Z",
|
35
|
+
"updated_at":"2017-03-07T06:06:25.000Z",
|
36
|
+
"shopify_id":null,
|
37
|
+
"confirmation_url":null
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
@@ -2,6 +2,8 @@ current_widget_store_subscription_recurring_charge:
|
|
2
2
|
shop: widget_store
|
3
3
|
subscription: current_widget_store_subscription
|
4
4
|
status: <%= DiscoApp::RecurringApplicationCharge.statuses[:active] %>
|
5
|
+
created_at: "2017-03-07T06:06:25.000Z"
|
6
|
+
updated_at: "2017-03-07T06:06:25.000Z"
|
5
7
|
|
6
8
|
new_widget_store_subscription_recurring_charge:
|
7
9
|
shop: widget_store
|
@@ -1,6 +1,8 @@
|
|
1
1
|
widget_store:
|
2
2
|
shopify_domain: widgets.myshopify.com
|
3
3
|
shopify_token: 00000000000000000000000000000000
|
4
|
+
created_at: "2017-03-07T06:06:25.000Z"
|
5
|
+
updated_at: "2017-03-07T06:06:25.000Z"
|
4
6
|
data: '{ "country_name": "Australia", "timezone": "(GMT+10:00) Melbourne" }'
|
5
7
|
|
6
8
|
widget_store_dev:
|
@@ -13,6 +13,9 @@ current_widget_store_subscription:
|
|
13
13
|
trial_period_days: 14
|
14
14
|
status: <%= DiscoApp::Subscription.statuses[:active] %>
|
15
15
|
subscription_type: 0
|
16
|
+
created_at: "2017-03-07T06:06:25.000Z"
|
17
|
+
updated_at: "2017-03-07T06:06:25.000Z"
|
18
|
+
|
16
19
|
|
17
20
|
current_widget_store_dev_subscription:
|
18
21
|
shop: widget_store_dev
|
@@ -11,11 +11,11 @@ class DiscoApp::AppInstalledJobTest < ActionController::TestCase
|
|
11
11
|
stub_request(:get, "#{@shop.admin_url}/shop.json").to_return(status: 200, body: api_fixture('widget_store/shop').to_json)
|
12
12
|
stub_request(:get, "#{@shop.admin_url}/carrier_services.json").to_return(status: 200, body: api_fixture('widget_store/carrier_services').to_json)
|
13
13
|
stub_request(:post, "#{@shop.admin_url}/carrier_services.json").to_return(status: 200)
|
14
|
+
stub_request(:post, "https://api.discolabs.com/v1/app_subscriptions.json").to_return(status: 200)
|
14
15
|
end
|
15
16
|
|
16
17
|
def teardown
|
17
18
|
@shop = nil
|
18
|
-
|
19
19
|
WebMock.reset!
|
20
20
|
end
|
21
21
|
|
@@ -5,7 +5,7 @@ class DiscoApp::AppUninstalledJobTest < ActionController::TestCase
|
|
5
5
|
|
6
6
|
def setup
|
7
7
|
@shop = disco_app_shops(:widget_store)
|
8
|
-
|
8
|
+
stub_request(:post, "https://api.discolabs.com/v1/app_subscriptions.json").to_return(status: 200)
|
9
9
|
perform_enqueued_jobs do
|
10
10
|
DiscoApp::AppUninstalledJob.perform_later(@shop, {})
|
11
11
|
end
|
@@ -13,18 +13,18 @@ class DiscoApp::AppUninstalledJobTest < ActionController::TestCase
|
|
13
13
|
|
14
14
|
def teardown
|
15
15
|
@shop = nil
|
16
|
+
WebMock.reset!
|
16
17
|
end
|
17
18
|
|
18
19
|
test 'app uninstalled job changes shop status' do
|
19
|
-
assert_performed_jobs
|
20
|
+
assert_performed_jobs 2
|
20
21
|
@shop.reload
|
21
22
|
assert @shop.uninstalled?
|
22
23
|
end
|
23
24
|
|
24
25
|
test 'app uninstalled job can be extended using concerns' do
|
25
|
-
assert_performed_jobs
|
26
|
+
assert_performed_jobs 2
|
26
27
|
@shop.reload
|
27
28
|
assert_equal 'Nowhere', @shop.data['country_name'] # Assert extended method called.
|
28
29
|
end
|
29
|
-
|
30
30
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class DiscoApp::SendSubscriptionJobTest < ActionController::TestCase
|
4
|
+
include ActiveJob::TestHelper
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@shop = disco_app_shops(:widget_store)
|
8
|
+
stub_request(:post, "https://api.discolabs.com/v1/app_subscriptions.json").to_return(status: 200)
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
@shop = nil
|
13
|
+
|
14
|
+
WebMock.reset!
|
15
|
+
end
|
16
|
+
|
17
|
+
test 'subscription job correctly sends request to API' do
|
18
|
+
perform_enqueued_jobs do
|
19
|
+
DiscoApp::SendSubscriptionJob.perform_later(@shop)
|
20
|
+
end
|
21
|
+
assert_requested(:post, "https://api.discolabs.com/v1/app_subscriptions.json", times: 1)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -10,6 +10,7 @@ ENV['SHOPIFY_APP_SECRET'] = 'b607d1f8b992dccb017f9315f07af9c4'
|
|
10
10
|
ENV['SHOPIFY_APP_REDIRECT_URI'] = 'https://test.example.com/shopify/auth/callback'
|
11
11
|
ENV['SHOPIFY_APP_SCOPE'] = 'read_products'
|
12
12
|
ENV['SHOPIFY_CHARGES_REAL'] = 'false'
|
13
|
+
ENV['DISCO_API_URL'] = 'https://api.discolabs.com/v1/'
|
13
14
|
|
14
15
|
require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
|
15
16
|
ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: disco_app
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.
|
4
|
+
version: 0.12.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gavin Ballard
|
@@ -100,14 +100,20 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '7.2'
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 7.2.3
|
104
107
|
type: :runtime
|
105
108
|
prerelease: false
|
106
109
|
version_requirements: !ruby/object:Gem::Requirement
|
107
110
|
requirements:
|
108
111
|
- - "~>"
|
109
112
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
113
|
+
version: '7.2'
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 7.2.3
|
111
117
|
- !ruby/object:Gem::Dependency
|
112
118
|
name: puma
|
113
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -526,6 +532,8 @@ files:
|
|
526
532
|
- app/assets/stylesheets/disco_app/ui-kit/_ui-layout.scss
|
527
533
|
- app/assets/stylesheets/disco_app/ui-kit/_ui-tabs.scss
|
528
534
|
- app/assets/stylesheets/disco_app/ui-kit/_ui-type.scss
|
535
|
+
- app/clients/disco_app/api_client.rb
|
536
|
+
- app/clients/disco_app/disco_api_error.rb
|
529
537
|
- app/controllers/disco_app/admin/app_settings_controller.rb
|
530
538
|
- app/controllers/disco_app/admin/application_controller.rb
|
531
539
|
- app/controllers/disco_app/admin/concerns/app_settings_controller.rb
|
@@ -558,6 +566,7 @@ files:
|
|
558
566
|
- app/jobs/disco_app/concerns/synchronise_resources_job.rb
|
559
567
|
- app/jobs/disco_app/concerns/synchronise_webhooks_job.rb
|
560
568
|
- app/jobs/disco_app/render_asset_group_job.rb
|
569
|
+
- app/jobs/disco_app/send_subscription_job.rb
|
561
570
|
- app/jobs/disco_app/shop_job.rb
|
562
571
|
- app/jobs/disco_app/shop_update_job.rb
|
563
572
|
- app/jobs/disco_app/subscription_changed_job.rb
|
@@ -587,6 +596,7 @@ files:
|
|
587
596
|
- app/services/disco_app/carrier_request_service.rb
|
588
597
|
- app/services/disco_app/charges_service.rb
|
589
598
|
- app/services/disco_app/proxy_service.rb
|
599
|
+
- app/services/disco_app/request_validation_service.rb
|
590
600
|
- app/services/disco_app/subscription_service.rb
|
591
601
|
- app/services/disco_app/webhook_service.rb
|
592
602
|
- app/views/disco_app/admin/app_settings/edit.html.erb
|
@@ -613,7 +623,7 @@ files:
|
|
613
623
|
- app/views/layouts/application.html.erb
|
614
624
|
- app/views/layouts/embedded_app.html.erb
|
615
625
|
- app/views/layouts/embedded_app_modal.html.erb
|
616
|
-
- app/views/sessions/new.html.erb
|
626
|
+
- app/views/shopify_app/sessions/new.html.erb
|
617
627
|
- config/routes.rb
|
618
628
|
- db/migrate/20150525000000_create_shops_if_not_existent.rb
|
619
629
|
- lib/disco_app.rb
|
@@ -641,12 +651,14 @@ files:
|
|
641
651
|
- lib/generators/disco_app/templates/root/CHECKS
|
642
652
|
- lib/generators/disco_app/templates/root/Procfile
|
643
653
|
- lib/generators/disco_app/templates/views/home/index.html.erb
|
654
|
+
- lib/tasks/api.rake
|
644
655
|
- lib/tasks/carrier_service.rake
|
645
656
|
- lib/tasks/database.rake
|
646
657
|
- lib/tasks/sessions.rake
|
647
658
|
- lib/tasks/shops.rake
|
648
659
|
- lib/tasks/start.rake
|
649
660
|
- lib/tasks/webhooks.rake
|
661
|
+
- test/clients/disco_app/api_client_test.rb
|
650
662
|
- test/controllers/disco_app/admin/shops_controller_test.rb
|
651
663
|
- test/controllers/disco_app/charges_controller_test.rb
|
652
664
|
- test/controllers/disco_app/install_controller_test.rb
|
@@ -718,6 +730,7 @@ files:
|
|
718
730
|
- test/dummy/public/422.html
|
719
731
|
- test/dummy/public/500.html
|
720
732
|
- test/dummy/public/favicon.ico
|
733
|
+
- test/fixtures/api/subscriptions/valid_request.json
|
721
734
|
- test/fixtures/api/widget_store/assets/create_script_tag_js_request.json
|
722
735
|
- test/fixtures/api/widget_store/assets/create_script_tag_js_response.json
|
723
736
|
- test/fixtures/api/widget_store/assets/create_script_tag_request.json
|
@@ -781,6 +794,7 @@ files:
|
|
781
794
|
- test/integration/synchronises_test.rb
|
782
795
|
- test/jobs/disco_app/app_installed_job_test.rb
|
783
796
|
- test/jobs/disco_app/app_uninstalled_job_test.rb
|
797
|
+
- test/jobs/disco_app/send_subscription_job_test.rb
|
784
798
|
- test/jobs/disco_app/synchronise_carrier_service_job_test.rb
|
785
799
|
- test/jobs/disco_app/synchronise_webhooks_job_test.rb
|
786
800
|
- test/models/disco_app/can_be_liquified_test.rb
|
@@ -884,6 +898,7 @@ test_files:
|
|
884
898
|
- test/dummy/config/database.gitlab-ci.yml
|
885
899
|
- test/dummy/config/application.rb
|
886
900
|
- test/fixtures/liquid/model.liquid
|
901
|
+
- test/fixtures/api/subscriptions/valid_request.json
|
887
902
|
- test/fixtures/api/widget_store/carrier_services.json
|
888
903
|
- test/fixtures/api/widget_store/assets/create_test_js_response.json
|
889
904
|
- test/fixtures/api/widget_store/assets/create_widget_js_response.json
|
@@ -949,6 +964,7 @@ test_files:
|
|
949
964
|
- test/disco_app_test.rb
|
950
965
|
- test/jobs/disco_app/app_uninstalled_job_test.rb
|
951
966
|
- test/jobs/disco_app/synchronise_carrier_service_job_test.rb
|
967
|
+
- test/jobs/disco_app/send_subscription_job_test.rb
|
952
968
|
- test/jobs/disco_app/synchronise_webhooks_job_test.rb
|
953
969
|
- test/jobs/disco_app/app_installed_job_test.rb
|
954
970
|
- test/controllers/disco_app/install_controller_test.rb
|
@@ -959,6 +975,7 @@ test_files:
|
|
959
975
|
- test/controllers/proxy_controller_test.rb
|
960
976
|
- test/controllers/home_controller_test.rb
|
961
977
|
- test/integration/synchronises_test.rb
|
978
|
+
- test/clients/disco_app/api_client_test.rb
|
962
979
|
- test/models/disco_app/plan_test.rb
|
963
980
|
- test/models/disco_app/has_metafields_test.rb
|
964
981
|
- test/models/disco_app/shop_test.rb
|
@@ -1,28 +0,0 @@
|
|
1
|
-
<% provide(:title, "Install #{DiscoApp.configuration.app_name}") %>
|
2
|
-
|
3
|
-
<%= form_tag shopify_app.login_path do %>
|
4
|
-
<div class="ui-empty-state">
|
5
|
-
<section class="ui-empty-state__section">
|
6
|
-
<div class="ui-empty-state__subsection">
|
7
|
-
<h1 class="ui-empty-state__title">Install <%= DiscoApp.configuration.app_name %></h1>
|
8
|
-
<h2 class="ui-empty-state__subtitle">Enter your Shopify store name below to get started.</h2>
|
9
|
-
</div>
|
10
|
-
<div class="ui-empty-state__subsection">
|
11
|
-
<div class="next-grid">
|
12
|
-
<div class="next-grid__cell" style="text-align: right;">
|
13
|
-
<label for="shop" class="next-input__help-text">https://</label>
|
14
|
-
</div>
|
15
|
-
<div class="next-grid__cell">
|
16
|
-
<input type="text" class="form-control" id="shop" name="shop" placeholder="your-store" autocomplete="off" autofocus="on" />
|
17
|
-
</div>
|
18
|
-
<div class="next-grid__cell" style="text-align: left;">
|
19
|
-
<label for="shop" class="next-input__help-text">.myshopify.com</label>
|
20
|
-
</div>
|
21
|
-
</div>
|
22
|
-
</div>
|
23
|
-
<div class="ui-empty-state__subsection">
|
24
|
-
<button type="submit" class="btn btn-large btn-primary">Install</button>
|
25
|
-
</div>
|
26
|
-
</section>
|
27
|
-
</div>
|
28
|
-
<% end %>
|