app_manager 0.1.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +16 -1
- data/README.md +110 -1
- data/app/controllers/app_manager/application_controller.rb +7 -0
- data/app/controllers/app_manager/banners_controller.rb +7 -2
- data/app/controllers/app_manager/charges_controller.rb +122 -0
- data/app/controllers/app_manager/plans_controller.rb +157 -0
- data/app/controllers/concerns/app_manager/authenticate.rb +10 -0
- data/app_manager.gemspec +2 -2
- data/config/routes.rb +14 -1
- data/lib/app_manager/actions.rb +10 -0
- data/lib/app_manager/api_cache_handler.rb +67 -0
- data/lib/app_manager/client/banners.rb +1 -1
- data/lib/app_manager/client/connection.rb +37 -3
- data/lib/app_manager/client/plans.rb +40 -0
- data/lib/app_manager/client.rb +9 -3
- data/lib/app_manager/config.rb +82 -0
- data/lib/app_manager/fail_safe.rb +324 -0
- data/lib/app_manager/graphql_helper.rb +125 -0
- data/lib/app_manager/model.rb +98 -0
- data/lib/app_manager/protection.rb +21 -0
- data/lib/app_manager/railtie.rb +10 -0
- data/lib/app_manager/response_cache.rb +40 -0
- data/lib/app_manager/tasks/sync/local_app_manager.rake +12 -0
- data/lib/app_manager/version.rb +1 -1
- data/lib/app_manager.rb +22 -2
- data/lib/generators/app_manager/install/install_generator.rb +8 -0
- data/lib/generators/app_manager/install/templates/app_manager.rb.tt +61 -0
- metadata +45 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d1ca5e305b62425f916bcb20e5677a2bc0be1e4b16f2d68879dcce443da5ffe
|
4
|
+
data.tar.gz: 1d174e5fbd19029a498c539ae908089dfcab7dea8b4d2f43d8e3bf48b9b354ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1b846e8b38a8fcb8301715c137f23e875828195c9b73bca8e1bbb814cf66fee9565690a296465757955d267fc290f574ba3434a63449657444c52144e0ad7c31
|
7
|
+
data.tar.gz: 00cdb184943ace69f37c5e166950791e193c67e071a88692a7b81d45de79a5d648ee2c70887c5f3cc1eea1d2ec5df82a7632aa9ae241c7f518c7f8ef52f31abe
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
app_manager (0.
|
4
|
+
app_manager (1.0.0)
|
5
5
|
httparty
|
6
|
+
kaminari (>= 0.16.3)
|
6
7
|
rails (>= 5.2.0)
|
8
|
+
sqlite3 (~> 1.3.0)
|
7
9
|
|
8
10
|
GEM
|
9
11
|
remote: https://rubygems.org/
|
@@ -94,6 +96,18 @@ GEM
|
|
94
96
|
i18n (1.10.0)
|
95
97
|
concurrent-ruby (~> 1.0)
|
96
98
|
io-wait (0.2.1)
|
99
|
+
kaminari (1.2.2)
|
100
|
+
activesupport (>= 4.1.0)
|
101
|
+
kaminari-actionview (= 1.2.2)
|
102
|
+
kaminari-activerecord (= 1.2.2)
|
103
|
+
kaminari-core (= 1.2.2)
|
104
|
+
kaminari-actionview (1.2.2)
|
105
|
+
actionview
|
106
|
+
kaminari-core (= 1.2.2)
|
107
|
+
kaminari-activerecord (1.2.2)
|
108
|
+
activerecord
|
109
|
+
kaminari-core (= 1.2.2)
|
110
|
+
kaminari-core (1.2.2)
|
97
111
|
loofah (2.15.0)
|
98
112
|
crass (~> 1.0.2)
|
99
113
|
nokogiri (>= 1.5.9)
|
@@ -178,6 +192,7 @@ GEM
|
|
178
192
|
diff-lcs (>= 1.2.0, < 2.0)
|
179
193
|
rspec-support (~> 3.11.0)
|
180
194
|
rspec-support (3.11.0)
|
195
|
+
sqlite3 (1.3.13)
|
181
196
|
strscan (3.0.1)
|
182
197
|
thor (1.2.1)
|
183
198
|
timeout (0.2.0)
|
data/README.md
CHANGED
@@ -18,17 +18,126 @@ Step 2) Execute following in terminal:
|
|
18
18
|
rails g app_manager:install
|
19
19
|
|
20
20
|
|
21
|
-
this install command will mount routes in your rails route.
|
21
|
+
this install command will mount routes in your rails route and will create a config file at ```config/initializers/app_manager.rb```
|
22
22
|
|
23
|
+
```
|
24
|
+
AppManager.configure do |config|
|
25
|
+
config.enable_caching = false # Optional, True to enable app-manager api response caching, default is enabled from gem
|
26
|
+
config.expires_in = 1.days # Optional, Example: 30.seconds, 5.minutes or 2.days Default caching is for 1.days from gem
|
27
|
+
config.app_url = '' # App URL like https://volumediscount.hulkapps.dev/ or #https://5044-2409-4052-209a-69da-9d7-925a-9418-a9c3.ngrok.io
|
28
|
+
config.shopify_api_key = '' # Shopify api key of app
|
29
|
+
config.shopify_api_version = '' # Must be 2022-04 or latest
|
30
|
+
config.shopify_table_name = 'shops' # Table name which is generated by shopify mostly it is 'shops'
|
31
|
+
config.shopify_domain_field = 'shopify_domain' #shopify domain field
|
32
|
+
config.plan_id_or_name_field = 'plan_id'
|
33
|
+
config.field_names = {
|
34
|
+
'name' => 'shopify_domain', # demo-rahul-tiwari.myshopify.com
|
35
|
+
'shopify_email' => 'email', # rahul.t@hulkapps.com
|
36
|
+
'shopify_token' => 'shopify_token',
|
37
|
+
'shopify_plan' => 'plan_name', # partner_test
|
38
|
+
'plan_id' => 'plan_id', # 1. t
|
39
|
+
'created_at' => 'created_at', # 2022-04-15 10:43:05
|
40
|
+
'trial_activated_at' => 'trial_activated_at' # field name that stores trial start/activated date
|
41
|
+
}
|
42
|
+
config.plan_features = [
|
43
|
+
{
|
44
|
+
"uuid" => "b48a3a6c-c1fb-11ec-9d64-0242ac120002",
|
45
|
+
"name" => "Features 1",
|
46
|
+
"slug" => "feature-1",
|
47
|
+
"description" => "Feature Description",
|
48
|
+
"value_type" => "integer",
|
49
|
+
"format" => "count",
|
50
|
+
"display_order" => 1
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"uuid" => "9f18f95a-bfaf-11ec-9d64-0242ac120002",
|
54
|
+
"name" => "Features 2",
|
55
|
+
"slug" => "feature-2",
|
56
|
+
"description" => "Feature Description",
|
57
|
+
"value_type" => "boolean",
|
58
|
+
"format" => "percentage",
|
59
|
+
"display_order" => 2
|
60
|
+
},
|
61
|
+
{
|
62
|
+
"uuid" => "9f190a26-bfaf-11ec-9d64-0242ac120002",
|
63
|
+
"name" => "Features 3",
|
64
|
+
"slug" => "feature-3",
|
65
|
+
"description" => "Feature Description",
|
66
|
+
"value_type" => "string",
|
67
|
+
"format" => "string",
|
68
|
+
"display_order" => 3
|
69
|
+
},
|
70
|
+
{
|
71
|
+
"uuid" => "9f191340-bfaf-11ec-9d64-0242ac12000",
|
72
|
+
"name" => "Features 4",
|
73
|
+
"slug" => "feature-4",
|
74
|
+
"description" => "Feature Description",
|
75
|
+
"value_type" => "array",
|
76
|
+
"values" => [
|
77
|
+
"val 1",
|
78
|
+
"val 2"
|
79
|
+
],
|
80
|
+
"format" => "string",
|
81
|
+
"display_order" => 4
|
82
|
+
}
|
83
|
+
] #Required, Values type : integer, boolean, string, array
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
|
88
|
+
Please note: Your shops table must have 'plan_id' as int/bigint and 'trial_activated_at' as datetime otherwise add its migration. Sample example:
|
89
|
+
```
|
90
|
+
class AddPlanIdToShops < ActiveRecord::Migration[5.2]
|
91
|
+
def change
|
92
|
+
add_column :shops, :plan_id, :bigint
|
93
|
+
add_column :shops, :trial_activated_at, :datetime
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
and map these new fields in 'config.field_names' in above initializer file.
|
23
99
|
|
24
100
|
Step 3) You must have to set ENV variable with key 'APP_MANAGER_ACCESS_TOKEN' in your application.yml or .env file like this:
|
25
101
|
|
26
102
|
APP_MANAGER_ACCESS_TOKEN: 'XXXXXXXXXXXXXXXXXXX'
|
27
103
|
APP_MANAGER_API_URL: 'https://XXXXXXXX.com'
|
28
104
|
|
105
|
+
Steps 4) App Manager provides a rake task that must needs to include in your existing app cron for every 10 minutes. Like if you are using [whenever](https://github.com/javan/whenever) gem then you must need to include following rake take in your 'schedule.rb' (created by whenever gem)
|
106
|
+
|
107
|
+
```
|
108
|
+
every 10.minutes do
|
109
|
+
rake 'sync:local_app_manager'
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
Steps 5) App Manager provides a helper module which needs to include in your 'shop' model something like below
|
114
|
+
|
115
|
+
```
|
116
|
+
class Shop < ActiveRecord::Base
|
117
|
+
include AppManager::Model
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
29
121
|
|
30
122
|
## Usage
|
31
123
|
|
124
|
+
Using above 5th step you can access following methods from your shop object.
|
125
|
+
|
126
|
+
```
|
127
|
+
@shop.has_plan # return true or false
|
128
|
+
|
129
|
+
@shop.plan_features # return array of plan features of your shop's plan
|
130
|
+
|
131
|
+
@shop.has_feature('feature-1') # provide slug of feature which you set in your 'config.plan_features' in config/initializers/app_manager.rb
|
132
|
+
#return true/false
|
133
|
+
|
134
|
+
@shop.get_feature('feature-3') # provide slug of feature and this returns value of feature which is set in portal.
|
135
|
+
|
136
|
+
@shop.get_remaining_days # return integer based on trial activated date.
|
137
|
+
```
|
138
|
+
|
139
|
+
|
140
|
+
|
32
141
|
#### As a helper:
|
33
142
|
|
34
143
|
|
@@ -1,5 +1,12 @@
|
|
1
1
|
module AppManager
|
2
2
|
class ApplicationController < ActionController::Base
|
3
|
+
include AppManager::Actions
|
4
|
+
cache_response_of_app_manager
|
3
5
|
protect_from_forgery with: :exception
|
6
|
+
include AppManager::Authenticate
|
7
|
+
# rescue_from StandardError do |exception|
|
8
|
+
# render json: { :error => exception.message }, :status => 500
|
9
|
+
# end
|
10
|
+
|
4
11
|
end
|
5
12
|
end
|
@@ -3,8 +3,13 @@ require_dependency "app_manager/application_controller"
|
|
3
3
|
module AppManager
|
4
4
|
class BannersController < ApplicationController
|
5
5
|
def marketing_banner
|
6
|
-
|
7
|
-
|
6
|
+
begin
|
7
|
+
banner_obj = AppManager::Client.new
|
8
|
+
render json: banner_obj.get_banners
|
9
|
+
rescue Exception => e
|
10
|
+
@fs = AppManager::FailSafe.new
|
11
|
+
render json: @fs.get_local_app_structures
|
12
|
+
end
|
8
13
|
end
|
9
14
|
end
|
10
15
|
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require_dependency "app_manager/application_controller"
|
2
|
+
|
3
|
+
module AppManager
|
4
|
+
class ChargesController < ApplicationController
|
5
|
+
include HTTParty
|
6
|
+
skip_before_action :verify_authenticity_token, :only => [:process_plan]
|
7
|
+
before_action :params_permit
|
8
|
+
require 'time'
|
9
|
+
require 'rack'
|
10
|
+
|
11
|
+
def process_plan
|
12
|
+
if params[:shop].present? && params[:plan_id].present?
|
13
|
+
@shop = shop_data
|
14
|
+
if !@shop.nil?
|
15
|
+
plan_obj = AppManager::Client.new
|
16
|
+
plan_data = plan_obj.get_plan(params[:plan_id],params[:shop])
|
17
|
+
if plan_data.present? && plan_data.is_a?(Hash)
|
18
|
+
request_data = {'shop' => @shop.shopify_domain, 'timestamp' => Time.now.to_i, 'plan' => params[:plan_id]}
|
19
|
+
return_url = "#{app_url}#{plan_callback_path}?#{Rack::Utils.build_query(request_data)}"
|
20
|
+
gq_obj = AppManager::GraphqlHelper.new(@shop.shopify_domain,@shop.shopify_token)
|
21
|
+
data = gq_obj.recurring_charge_api_call(plan_data,return_url,@shop)
|
22
|
+
if !data["errors"].present? && (data["data"].present? && data["data"]["appSubscriptionCreate"].present? && ( !data["data"]["appSubscriptionCreate"]["userErrors"].any? && data["data"]["appSubscriptionCreate"]["confirmationUrl"]))
|
23
|
+
redirect_charge = data["data"]["appSubscriptionCreate"]["confirmationUrl"]
|
24
|
+
render json: {'redirect_url' => redirect_charge}
|
25
|
+
else
|
26
|
+
render json: {'error' => data["errors"].as_json}
|
27
|
+
end
|
28
|
+
else
|
29
|
+
render json: {'error' => data["errors"]}
|
30
|
+
end
|
31
|
+
else
|
32
|
+
render json: {'error' => 'Shop not found'}
|
33
|
+
end
|
34
|
+
else
|
35
|
+
render json: {'error' => 'Missing Shop domain or plan id in params'}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def callback
|
41
|
+
if params[:charge_id].present? && params[:shop].present? && params[:plan].present?
|
42
|
+
@shop = shop_data
|
43
|
+
shopify_token = @field_names['shopify_token']
|
44
|
+
shopify_domain = @field_names['name']
|
45
|
+
if !@shop.nil?
|
46
|
+
headers = {"X-Shopify-Access-Token" => @shop[shopify_token]}
|
47
|
+
charges = HTTParty.get('https://'+@shop[shopify_domain]+'/admin/api/'+@api_version+'/recurring_application_charges/'+params[:charge_id]+'.json', :headers => headers)
|
48
|
+
|
49
|
+
if charges.parsed_response && charges.parsed_response.is_a?(Hash) && charges.parsed_response.has_key?('recurring_application_charge')
|
50
|
+
|
51
|
+
plan_obj = AppManager::Client.new
|
52
|
+
plan_data = plan_obj.get_plan(params[:plan],params[:shop])
|
53
|
+
|
54
|
+
charge = charges.parsed_response['recurring_application_charge']
|
55
|
+
charge['charge_id'] = charge['id'];
|
56
|
+
charge['type'] = 'recurring';
|
57
|
+
charge['plan_id'] = params[:plan];
|
58
|
+
charge['shop_domain'] = params[:shop];
|
59
|
+
charge['interval'] = plan_data['interval']['value'];
|
60
|
+
|
61
|
+
if !@plan_field.nil?
|
62
|
+
begin
|
63
|
+
plan_obj.cancel_charge(@shop[shopify_domain],@shop[@plan_field])
|
64
|
+
rescue Exception => e
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
charge_ob = AppManager::Client.new(nil,json_req=true)
|
69
|
+
response = charge_ob.store_charge(charge.to_json)
|
70
|
+
|
71
|
+
if response['message'] == "success"
|
72
|
+
model.update(@plan_field => params[:plan])
|
73
|
+
end
|
74
|
+
redirect_to "/?shop=#{params[:shop]}"
|
75
|
+
else
|
76
|
+
|
77
|
+
render json: {'error' => 'Invalid shopify charge'}
|
78
|
+
end
|
79
|
+
else
|
80
|
+
render json: {'error' => 'Shop not found'}
|
81
|
+
end
|
82
|
+
else
|
83
|
+
render json: {'error' => 'Invalid params'}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def params_permit
|
90
|
+
params.permit!
|
91
|
+
end
|
92
|
+
|
93
|
+
def model
|
94
|
+
@models = ActiveRecord::Base.connection.tables
|
95
|
+
@config_table = AppManager.configuration.shopify_table_name
|
96
|
+
@shopify_domain = AppManager.configuration.shopify_domain_field
|
97
|
+
@plan_field = AppManager.configuration.plan_id_or_name_field
|
98
|
+
@field_names = AppManager.configuration.field_names
|
99
|
+
@api_version = AppManager.configuration.shopify_api_version
|
100
|
+
if @models.include?(@config_table) && !@plan_field.nil?
|
101
|
+
return @config_table.classify.constantize
|
102
|
+
else
|
103
|
+
return nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def shop_data
|
108
|
+
if model
|
109
|
+
return model.where(@shopify_domain => params[:shop]).first rescue nil
|
110
|
+
else
|
111
|
+
return nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def app_url
|
116
|
+
AppManager.configuration.app_url
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require_dependency "app_manager/application_controller"
|
2
|
+
|
3
|
+
module AppManager
|
4
|
+
class PlansController < ApplicationController
|
5
|
+
skip_before_action :verify_authenticity_token, :only => [:active_without_plan,:burst_cache,:fail_safe_backup]
|
6
|
+
def index
|
7
|
+
render :json => {"features" => AppManager.configuration.plan_features || []}
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def plans
|
12
|
+
active_plan_id_or_name = shopify_plan = plan = nil
|
13
|
+
default_plan_id = nil
|
14
|
+
plan_obj = AppManager::Client.new
|
15
|
+
plans = plan_obj.get_plans
|
16
|
+
if params[:shop_domain].present? && !AppManager.configuration.plan_features.nil?
|
17
|
+
@shop = shop_data
|
18
|
+
if !@shop.nil?
|
19
|
+
active_plan_id_or_name = @shop[@plan_field]
|
20
|
+
if !@field_names.nil? && @field_names.has_key?('shopify_plan') && !@field_names.nil?
|
21
|
+
shopify_plan_field = AppManager.configuration.field_names['shopify_plan']
|
22
|
+
shopify_plan = @shop[shopify_plan_field] rescue nil
|
23
|
+
plan = plans.any? && !active_plan_id_or_name.nil? ? (plans.find{|x| x["id"] == active_plan_id_or_name}.present? ? plans.find{|x| x["id"] == active_plan_id_or_name} : nil ) : nil
|
24
|
+
end
|
25
|
+
else
|
26
|
+
Rails.logger.info "APP MANGAGER >>>> Either model is defined wrong or config.plan_id_or_name_field is nil in initializer ==="
|
27
|
+
end
|
28
|
+
else
|
29
|
+
Rails.logger.info "APP MANGAGER >>>> Either params missing store_domain or config.plan_features is nil in initializer ==="
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
default_plan_id = 0;
|
34
|
+
default_plan_data = plans.select{|x| x['interval'] == 'EVERY_30_DAYS' }.sort_by { |k| -k["price"] }
|
35
|
+
store_base_plan = default_plan_data.map{|e| e['store_base_plan']}.first rescue false
|
36
|
+
|
37
|
+
if store_base_plan
|
38
|
+
begin
|
39
|
+
shopify_plans = default_plan_data.map { |e| e.map { |key, value| [e['shopify_plans'], e['id']] }.to_h }
|
40
|
+
shopify_plans.each do |shp|
|
41
|
+
if shp.keys && shp.keys.first && shp.keys.first.class == Array
|
42
|
+
if shp.keys.first.include?(shopify_plan) && shp.values && shp.values.first && shp.values.first.to_i > 0
|
43
|
+
default_plan_id = shp.values.first.to_i
|
44
|
+
break
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue Exception => e
|
49
|
+
Rails.logger.info "APP MANGAGER >>>> ERROR #{e.inspect}"
|
50
|
+
end
|
51
|
+
else
|
52
|
+
default_plan_id = default_plan_data.map{|e| e['id']}.first if default_plan_data.any? rescue nil
|
53
|
+
end
|
54
|
+
|
55
|
+
response = {
|
56
|
+
'plans' => plan_obj.get_plans,
|
57
|
+
'shopify_plan' => shopify_plan,
|
58
|
+
'plan' => plan,
|
59
|
+
'default_plan_id' => default_plan_id
|
60
|
+
}
|
61
|
+
render json: response
|
62
|
+
end
|
63
|
+
|
64
|
+
def users
|
65
|
+
if model
|
66
|
+
data = model.page(params[:page]).per(10)
|
67
|
+
users = {
|
68
|
+
"current_page" => data.current_page,
|
69
|
+
"data" => data.collect{|st| @field_names.map{ |key, value| [key, "#{st[value]}"] }.to_h },
|
70
|
+
"first_page_url" => "#{app_url}/api/app-manager/users?page=1",
|
71
|
+
"from" => data.total_pages ? 1 : nil,
|
72
|
+
"last_page" => data.total_pages,
|
73
|
+
"last_page_url" => data.total_pages ? "#{app_url}/api/app-manager/users?page=#{data.total_pages}" : nil,
|
74
|
+
"links"=> '',
|
75
|
+
"next_page_url" => data.next_page ? "#{app_url}/api/app-manager/users?page=#{data.next_page}" : nil,
|
76
|
+
"path" => "#{app_url}/api/app-manager/users",
|
77
|
+
"per_page" => 1,
|
78
|
+
"prev_page_url" => data.prev_page ? "#{app_url}/api/app-manager/users?page=#{data.prev_page}" : nil,
|
79
|
+
"to" => data.total_pages,
|
80
|
+
"total" => data.total_pages
|
81
|
+
}
|
82
|
+
render json: users
|
83
|
+
|
84
|
+
else
|
85
|
+
render json: nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def active_without_plan
|
90
|
+
if params[:shop_domain].present? && params[:plan_id].present? && model
|
91
|
+
@trial_activated_field = AppManager.configuration.field_names['trial_activated_at']
|
92
|
+
model.update(@plan_field => params[:plan_id],@trial_activated_field => DateTime.now)
|
93
|
+
render json: {'status' => true}
|
94
|
+
else
|
95
|
+
render json: {'status' => false,'error' => 'Shop not found or missing shop'}, status: 422
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def burst_cache
|
101
|
+
AppManager.clear_cache
|
102
|
+
head :ok
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def fail_safe_backup
|
107
|
+
params_permit
|
108
|
+
@fs = AppManager::FailSafe.new
|
109
|
+
begin
|
110
|
+
@fs.sync_app_manager
|
111
|
+
rescue Exception => e
|
112
|
+
Rails.logger.info "APP MANGAGER >>>> LOCAL DB couldn't sync with POTAL DB #{e.inspect}"
|
113
|
+
end
|
114
|
+
|
115
|
+
begin
|
116
|
+
@fs.save_api_data(params)
|
117
|
+
rescue Exception => e
|
118
|
+
Rails.logger.info "APP MANGAGER >>>> #{e.inspect}"
|
119
|
+
end
|
120
|
+
head :ok
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def params_permit
|
127
|
+
params.permit!
|
128
|
+
end
|
129
|
+
|
130
|
+
def model
|
131
|
+
@models = ActiveRecord::Base.connection.tables
|
132
|
+
@config_table = AppManager.configuration.shopify_table_name
|
133
|
+
@shopify_domain = AppManager.configuration.shopify_domain_field
|
134
|
+
@plan_field = AppManager.configuration.plan_id_or_name_field
|
135
|
+
@field_names = AppManager.configuration.field_names
|
136
|
+
@api_version = AppManager.configuration.shopify_api_version
|
137
|
+
if @models.include?(@config_table) && !@plan_field.nil?
|
138
|
+
return @config_table.classify.constantize
|
139
|
+
else
|
140
|
+
return nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def shop_data
|
145
|
+
if model
|
146
|
+
return model.where(@shopify_domain => params[:shop_domain]).first rescue nil
|
147
|
+
else
|
148
|
+
return nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def app_url
|
153
|
+
AppManager.configuration.app_url
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
data/app_manager.gemspec
CHANGED
@@ -32,8 +32,8 @@ Gem::Specification.new do |spec|
|
|
32
32
|
# spec.add_dependency "example-gem", "~> 1.0"
|
33
33
|
spec.add_dependency "httparty"
|
34
34
|
spec.add_dependency "rails", '>= 5.2.0'
|
35
|
-
|
36
|
-
|
35
|
+
spec.add_dependency "kaminari", '>= 0.16.3'
|
36
|
+
spec.add_dependency "sqlite3", '~> 1.3.0'
|
37
37
|
# For more information and examples about making a new gem, checkout our
|
38
38
|
# guide at: https://bundler.io/guides/creating_gem.html
|
39
39
|
end
|
data/config/routes.rb
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
AppManager::Engine.routes.draw do
|
2
2
|
scope 'api/app-manager' do
|
3
3
|
get 'marketing-banners' => 'banners#marketing_banner'
|
4
|
+
|
5
|
+
get 'plan-features' => 'plans#index'
|
6
|
+
get 'plans' => 'plans#plans'
|
7
|
+
get 'users' => 'plans#users'
|
8
|
+
post 'active-without-plan' => 'plans#active_without_plan'
|
9
|
+
post 'burst-cache' => 'plans#burst_cache'
|
10
|
+
post 'fail-safe-backup' => 'plans#fail_safe_backup'
|
11
|
+
get 'plan/process/:plan_id' => 'charges#process_plan'
|
12
|
+
get 'plan/callback' => 'charges#callback', as: :plan_callback
|
13
|
+
|
14
|
+
scope 'app-manager-api' do
|
15
|
+
post 'store-charge' => 'plans#store_charge'
|
16
|
+
end
|
4
17
|
end
|
5
|
-
end
|
18
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module AppManager
|
2
|
+
module Actions
|
3
|
+
class ApiCacheHandler
|
4
|
+
def initialize
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
def around(controller)
|
10
|
+
init(controller)
|
11
|
+
if should_response_cache?
|
12
|
+
log_info
|
13
|
+
render_cached_response
|
14
|
+
else
|
15
|
+
yield
|
16
|
+
@response_cache.write_cache(controller.response) if @request.get?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def should_response_cache?
|
23
|
+
return @request.get? && @response_cache.cached_response.present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def log_info
|
27
|
+
# processor = "#{@controller.class.name}##{@controller.action_name}".blue
|
28
|
+
# responder = Rainbow('API Response Cache').green
|
29
|
+
Rails.logger.info "=== #{@controller.class.name}##{@controller.action_name} response by App Manager Response Cache ==="
|
30
|
+
end
|
31
|
+
|
32
|
+
def init(controller)
|
33
|
+
@controller = controller
|
34
|
+
@request = controller.request
|
35
|
+
@response_cache = ResponseCache.new(cache_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def cache_path
|
39
|
+
@cache_path = "app-manager-cache"
|
40
|
+
|
41
|
+
path_only = @request.fullpath.split('?').first
|
42
|
+
@cache_path = "#{@cache_path}#{path_only}"
|
43
|
+
|
44
|
+
if AppManager.configuration.cache_by_headers.present?
|
45
|
+
headers = AppManager.configuration.cache_by_headers.map do |header|
|
46
|
+
@request.headers[header].to_s
|
47
|
+
end
|
48
|
+
headers_cache_path = headers.join('-')
|
49
|
+
@cache_path = "#{@cache_path}/#{headers_cache_path}"
|
50
|
+
end
|
51
|
+
|
52
|
+
@cache_path
|
53
|
+
end
|
54
|
+
|
55
|
+
def render_cached_response
|
56
|
+
body = @response_cache.body
|
57
|
+
status = @response_cache.status
|
58
|
+
headers = @response_cache.headers
|
59
|
+
headers.try(:each) do |key, value|
|
60
|
+
@controller.response.headers[key] = value
|
61
|
+
end
|
62
|
+
@controller.render plain: body, status: status
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -24,9 +24,19 @@ module AppManager
|
|
24
24
|
|
25
25
|
def request(http_method, path, options)
|
26
26
|
response = self.class.send(http_method, path, { body: options })
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
if path.include? "/get-status"
|
28
|
+
data = response
|
29
|
+
elsif path.include? "/sync-charge"
|
30
|
+
data = response.parsed_response
|
31
|
+
data = parse_data(response.parsed_response)
|
32
|
+
else
|
33
|
+
if response.code.to_s.start_with?('2') or response.code.to_s.start_with?('4')
|
34
|
+
data = response.parsed_response
|
35
|
+
data = parse_data(response.parsed_response)
|
36
|
+
else
|
37
|
+
data = response_from_failsafe(path,options)
|
38
|
+
end
|
39
|
+
end
|
30
40
|
end
|
31
41
|
|
32
42
|
def parse_data(original_data)
|
@@ -36,7 +46,31 @@ module AppManager
|
|
36
46
|
else
|
37
47
|
return original_data
|
38
48
|
end
|
49
|
+
end
|
39
50
|
|
51
|
+
def response_from_failsafe(path,options)
|
52
|
+
params = Rack::Utils.parse_query URI(path).query
|
53
|
+
path = path.delete_prefix('/')
|
54
|
+
path = path[/[^?]+/]
|
55
|
+
@fs = AppManager::FailSafe.new
|
56
|
+
case path
|
57
|
+
when "static-contents"
|
58
|
+
return @fs.get_local_app_structures
|
59
|
+
when 'plans'
|
60
|
+
return @fs.get_local_plans
|
61
|
+
when 'plan'
|
62
|
+
return @fs.get_local_plan(params)
|
63
|
+
when 'store-charge'
|
64
|
+
return @fs.store_local_charge(params,options)
|
65
|
+
when 'cancel-charge'
|
66
|
+
return @fs.store_cancel_charge(params,options)
|
67
|
+
when 'get-remaining-days'
|
68
|
+
return @fs.get_local_remaining_days(params,options)
|
69
|
+
when 'get-charge'
|
70
|
+
return @fs.get_local_charge(params,options)
|
71
|
+
else
|
72
|
+
return nil
|
73
|
+
end
|
40
74
|
end
|
41
75
|
|
42
76
|
end
|