app_manager 2.3.6 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3d64d0ffdff623bbbd97c138297df0607d4621926c02c42404103626ff3592b
4
- data.tar.gz: de6e2e35c943c8cfa30649173d126d3ed8274daad83c13e46e28b4ea689c9fea
3
+ metadata.gz: 8ef9a51f2af61a37129f4654a1b2d60c4542cbfd3e229d34754f748180a1e75e
4
+ data.tar.gz: 6ca85b33ff77d3043f0cc683ab93f033df7964e6844edbf5b706fe2f2adff17f
5
5
  SHA512:
6
- metadata.gz: d572b354aedc7762a4989e86ba513651705943130d4fe89483c4d11e2bbfa78be4894ac816b3ba3a7e6a0663d26df17735bab1cf9e6cade164837021582bdc8c
7
- data.tar.gz: 0c4c262796b39cb38c22dc902dba79124f411e631270ef3e0ed405b5aaf7cbc7c72f4786b61c104e2ff26d84ba2f80cb625acf0af7ecbecd4ccc1d4bfeabf0c2
6
+ metadata.gz: 2dbc7e0beb36d44be94178415ec4ea098bd5baf2ff799963987fa9df4d8bdb9044644a94a799568303ff8b089e8a878675ba318749cfc24adb0c705e3f51707c
7
+ data.tar.gz: e4dac67910df25ee2746af43fc70c394367da06ded486065140aa453c84d5873ded867ce3c94a6182fdbe265d4e271734cf0322eee3e1a01cece4a1de65e17c9
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- app_manager (2.3.6)
4
+ app_manager (2.4.0)
5
5
  activerecord-import (~> 1.4)
6
6
  httparty
7
7
  kaminari (>= 0.16.3)
@@ -10,31 +10,31 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- action_text-trix (2.1.15)
13
+ action_text-trix (2.1.16)
14
14
  railties
15
- actioncable (8.1.0)
16
- actionpack (= 8.1.0)
17
- activesupport (= 8.1.0)
15
+ actioncable (8.1.2)
16
+ actionpack (= 8.1.2)
17
+ activesupport (= 8.1.2)
18
18
  nio4r (~> 2.0)
19
19
  websocket-driver (>= 0.6.1)
20
20
  zeitwerk (~> 2.6)
21
- actionmailbox (8.1.0)
22
- actionpack (= 8.1.0)
23
- activejob (= 8.1.0)
24
- activerecord (= 8.1.0)
25
- activestorage (= 8.1.0)
26
- activesupport (= 8.1.0)
21
+ actionmailbox (8.1.2)
22
+ actionpack (= 8.1.2)
23
+ activejob (= 8.1.2)
24
+ activerecord (= 8.1.2)
25
+ activestorage (= 8.1.2)
26
+ activesupport (= 8.1.2)
27
27
  mail (>= 2.8.0)
28
- actionmailer (8.1.0)
29
- actionpack (= 8.1.0)
30
- actionview (= 8.1.0)
31
- activejob (= 8.1.0)
32
- activesupport (= 8.1.0)
28
+ actionmailer (8.1.2)
29
+ actionpack (= 8.1.2)
30
+ actionview (= 8.1.2)
31
+ activejob (= 8.1.2)
32
+ activesupport (= 8.1.2)
33
33
  mail (>= 2.8.0)
34
34
  rails-dom-testing (~> 2.2)
35
- actionpack (8.1.0)
36
- actionview (= 8.1.0)
37
- activesupport (= 8.1.0)
35
+ actionpack (8.1.2)
36
+ actionview (= 8.1.2)
37
+ activesupport (= 8.1.2)
38
38
  nokogiri (>= 1.8.5)
39
39
  rack (>= 2.2.4)
40
40
  rack-session (>= 1.0.1)
@@ -42,38 +42,38 @@ GEM
42
42
  rails-dom-testing (~> 2.2)
43
43
  rails-html-sanitizer (~> 1.6)
44
44
  useragent (~> 0.16)
45
- actiontext (8.1.0)
45
+ actiontext (8.1.2)
46
46
  action_text-trix (~> 2.1.15)
47
- actionpack (= 8.1.0)
48
- activerecord (= 8.1.0)
49
- activestorage (= 8.1.0)
50
- activesupport (= 8.1.0)
47
+ actionpack (= 8.1.2)
48
+ activerecord (= 8.1.2)
49
+ activestorage (= 8.1.2)
50
+ activesupport (= 8.1.2)
51
51
  globalid (>= 0.6.0)
52
52
  nokogiri (>= 1.8.5)
53
- actionview (8.1.0)
54
- activesupport (= 8.1.0)
53
+ actionview (8.1.2)
54
+ activesupport (= 8.1.2)
55
55
  builder (~> 3.1)
56
56
  erubi (~> 1.11)
57
57
  rails-dom-testing (~> 2.2)
58
58
  rails-html-sanitizer (~> 1.6)
59
- activejob (8.1.0)
60
- activesupport (= 8.1.0)
59
+ activejob (8.1.2)
60
+ activesupport (= 8.1.2)
61
61
  globalid (>= 0.3.6)
62
- activemodel (8.1.0)
63
- activesupport (= 8.1.0)
64
- activerecord (8.1.0)
65
- activemodel (= 8.1.0)
66
- activesupport (= 8.1.0)
62
+ activemodel (8.1.2)
63
+ activesupport (= 8.1.2)
64
+ activerecord (8.1.2)
65
+ activemodel (= 8.1.2)
66
+ activesupport (= 8.1.2)
67
67
  timeout (>= 0.4.0)
68
68
  activerecord-import (1.8.1)
69
69
  activerecord (>= 4.2)
70
- activestorage (8.1.0)
71
- actionpack (= 8.1.0)
72
- activejob (= 8.1.0)
73
- activerecord (= 8.1.0)
74
- activesupport (= 8.1.0)
70
+ activestorage (8.1.2)
71
+ actionpack (= 8.1.2)
72
+ activejob (= 8.1.2)
73
+ activerecord (= 8.1.2)
74
+ activesupport (= 8.1.2)
75
75
  marcel (~> 1.0)
76
- activesupport (8.1.0)
76
+ activesupport (8.1.2)
77
77
  base64
78
78
  bigdecimal
79
79
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -86,39 +86,41 @@ GEM
86
86
  securerandom (>= 0.3)
87
87
  tzinfo (~> 2.0, >= 2.0.5)
88
88
  uri (>= 0.13.1)
89
- addressable (2.8.0)
90
- public_suffix (>= 2.0.2, < 5.0)
89
+ addressable (2.8.8)
90
+ public_suffix (>= 2.0.2, < 8.0)
91
91
  base64 (0.3.0)
92
- bigdecimal (3.3.1)
92
+ bigdecimal (4.0.1)
93
93
  builder (3.3.0)
94
94
  coderay (1.1.3)
95
- concurrent-ruby (1.3.5)
96
- connection_pool (2.5.4)
97
- crack (0.4.5)
95
+ concurrent-ruby (1.3.6)
96
+ connection_pool (3.0.2)
97
+ crack (1.0.1)
98
+ bigdecimal
98
99
  rexml
99
100
  crass (1.0.6)
100
101
  csv (3.3.5)
101
- date (3.4.1)
102
- diff-lcs (1.5.0)
102
+ date (3.5.1)
103
+ diff-lcs (1.6.2)
103
104
  dotenv (2.1.2)
104
105
  drb (2.2.3)
105
- erb (5.1.3)
106
+ erb (6.0.1)
106
107
  erubi (1.13.1)
107
108
  globalid (1.3.0)
108
109
  activesupport (>= 6.1)
109
- hashdiff (1.0.1)
110
- httparty (0.23.2)
110
+ hashdiff (1.2.1)
111
+ httparty (0.24.2)
111
112
  csv
112
113
  mini_mime (>= 1.0.0)
113
114
  multi_xml (>= 0.5.2)
114
- i18n (1.14.7)
115
+ i18n (1.14.8)
115
116
  concurrent-ruby (~> 1.0)
116
- io-console (0.8.1)
117
- irb (1.15.2)
117
+ io-console (0.8.2)
118
+ irb (1.17.0)
118
119
  pp (>= 0.6.0)
120
+ prism (>= 1.3.0)
119
121
  rdoc (>= 4.0.0)
120
122
  reline (>= 0.4.2)
121
- json (2.15.2)
123
+ json (2.18.1)
122
124
  kaminari (1.2.2)
123
125
  activesupport (>= 4.1.0)
124
126
  kaminari-actionview (= 1.2.2)
@@ -132,7 +134,7 @@ GEM
132
134
  kaminari-core (= 1.2.2)
133
135
  kaminari-core (1.2.2)
134
136
  logger (1.7.0)
135
- loofah (2.24.1)
137
+ loofah (2.25.0)
136
138
  crass (~> 1.0.2)
137
139
  nokogiri (>= 1.12.0)
138
140
  mail (2.9.0)
@@ -142,13 +144,14 @@ GEM
142
144
  net-pop
143
145
  net-smtp
144
146
  marcel (1.1.0)
145
- method_source (1.0.0)
147
+ method_source (1.1.0)
146
148
  mini_mime (1.1.5)
147
149
  mini_portile2 (2.8.9)
148
- minitest (5.26.0)
149
- multi_xml (0.7.2)
150
- bigdecimal (~> 3.1)
151
- net-imap (0.5.12)
150
+ minitest (6.0.1)
151
+ prism (~> 1.5)
152
+ multi_xml (0.8.1)
153
+ bigdecimal (>= 3.1, < 5)
154
+ net-imap (0.6.3)
152
155
  date
153
156
  net-protocol
154
157
  net-pop (0.1.2)
@@ -157,45 +160,47 @@ GEM
157
160
  timeout
158
161
  net-smtp (0.5.1)
159
162
  net-protocol
160
- nio4r (2.7.4)
161
- nokogiri (1.18.10)
163
+ nio4r (2.7.5)
164
+ nokogiri (1.19.1)
162
165
  mini_portile2 (~> 2.8.2)
163
166
  racc (~> 1.4)
164
- nokogiri (1.18.10-x86_64-linux-gnu)
167
+ nokogiri (1.19.1-x86_64-linux-gnu)
165
168
  racc (~> 1.4)
166
169
  pp (0.6.3)
167
170
  prettyprint
168
171
  prettyprint (0.2.0)
169
- pry (0.14.1)
172
+ prism (1.9.0)
173
+ pry (0.16.0)
170
174
  coderay (~> 1.1)
171
175
  method_source (~> 1.0)
172
- psych (5.2.6)
176
+ reline (>= 0.6.0)
177
+ psych (5.3.1)
173
178
  date
174
179
  stringio
175
- public_suffix (4.0.6)
180
+ public_suffix (7.0.2)
176
181
  racc (1.8.1)
177
- rack (3.2.3)
182
+ rack (3.2.5)
178
183
  rack-session (2.1.1)
179
184
  base64 (>= 0.1.0)
180
185
  rack (>= 3.0.0)
181
186
  rack-test (2.2.0)
182
187
  rack (>= 1.3)
183
- rackup (2.2.1)
188
+ rackup (2.3.1)
184
189
  rack (>= 3)
185
- rails (8.1.0)
186
- actioncable (= 8.1.0)
187
- actionmailbox (= 8.1.0)
188
- actionmailer (= 8.1.0)
189
- actionpack (= 8.1.0)
190
- actiontext (= 8.1.0)
191
- actionview (= 8.1.0)
192
- activejob (= 8.1.0)
193
- activemodel (= 8.1.0)
194
- activerecord (= 8.1.0)
195
- activestorage (= 8.1.0)
196
- activesupport (= 8.1.0)
190
+ rails (8.1.2)
191
+ actioncable (= 8.1.2)
192
+ actionmailbox (= 8.1.2)
193
+ actionmailer (= 8.1.2)
194
+ actionpack (= 8.1.2)
195
+ actiontext (= 8.1.2)
196
+ actionview (= 8.1.2)
197
+ activejob (= 8.1.2)
198
+ activemodel (= 8.1.2)
199
+ activerecord (= 8.1.2)
200
+ activestorage (= 8.1.2)
201
+ activesupport (= 8.1.2)
197
202
  bundler (>= 1.15.0)
198
- railties (= 8.1.0)
203
+ railties (= 8.1.2)
199
204
  rails-dom-testing (2.3.0)
200
205
  activesupport (>= 5.0.0)
201
206
  minitest
@@ -203,48 +208,48 @@ GEM
203
208
  rails-html-sanitizer (1.6.2)
204
209
  loofah (~> 2.21)
205
210
  nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
206
- railties (8.1.0)
207
- actionpack (= 8.1.0)
208
- activesupport (= 8.1.0)
211
+ railties (8.1.2)
212
+ actionpack (= 8.1.2)
213
+ activesupport (= 8.1.2)
209
214
  irb (~> 1.13)
210
215
  rackup (>= 1.0.0)
211
216
  rake (>= 12.2)
212
217
  thor (~> 1.0, >= 1.2.2)
213
218
  tsort (>= 0.2)
214
219
  zeitwerk (~> 2.6)
215
- rake (13.0.6)
216
- rdoc (6.15.0)
220
+ rake (13.3.1)
221
+ rdoc (7.2.0)
217
222
  erb
218
223
  psych (>= 4.0.0)
219
224
  tsort
220
- reline (0.6.2)
225
+ reline (0.6.3)
221
226
  io-console (~> 0.5)
222
- rexml (3.2.5)
223
- rspec (3.11.0)
224
- rspec-core (~> 3.11.0)
225
- rspec-expectations (~> 3.11.0)
226
- rspec-mocks (~> 3.11.0)
227
- rspec-core (3.11.0)
228
- rspec-support (~> 3.11.0)
229
- rspec-expectations (3.11.0)
227
+ rexml (3.4.4)
228
+ rspec (3.13.2)
229
+ rspec-core (~> 3.13.0)
230
+ rspec-expectations (~> 3.13.0)
231
+ rspec-mocks (~> 3.13.0)
232
+ rspec-core (3.13.6)
233
+ rspec-support (~> 3.13.0)
234
+ rspec-expectations (3.13.5)
230
235
  diff-lcs (>= 1.2.0, < 2.0)
231
- rspec-support (~> 3.11.0)
232
- rspec-github (2.3.1)
236
+ rspec-support (~> 3.13.0)
237
+ rspec-github (3.0.0)
233
238
  rspec-core (~> 3.0)
234
- rspec-mocks (3.11.0)
239
+ rspec-mocks (3.13.7)
235
240
  diff-lcs (>= 1.2.0, < 2.0)
236
- rspec-support (~> 3.11.0)
237
- rspec-support (3.11.0)
241
+ rspec-support (~> 3.13.0)
242
+ rspec-support (3.13.7)
238
243
  securerandom (0.4.1)
239
- stringio (3.1.7)
240
- thor (1.4.0)
241
- timeout (0.4.3)
244
+ stringio (3.2.0)
245
+ thor (1.5.0)
246
+ timeout (0.6.0)
242
247
  tsort (0.2.0)
243
248
  tzinfo (2.0.6)
244
249
  concurrent-ruby (~> 1.0)
245
- uri (1.0.4)
250
+ uri (1.1.1)
246
251
  useragent (0.16.11)
247
- webmock (3.14.0)
252
+ webmock (3.26.1)
248
253
  addressable (>= 2.8.0)
249
254
  crack (>= 0.3.2)
250
255
  hashdiff (>= 0.4.0, < 2.0.0)
@@ -252,7 +257,7 @@ GEM
252
257
  base64
253
258
  websocket-extensions (>= 0.1.0)
254
259
  websocket-extensions (0.1.5)
255
- zeitwerk (2.7.3)
260
+ zeitwerk (2.7.5)
256
261
 
257
262
  PLATFORMS
258
263
  ruby
@@ -165,11 +165,11 @@ module AppManager
165
165
  old_plan_string = !old_plan_data.nil? ? "#{old_plan_data['name'].gsub(" ", "-")}-#{old_plan_data['interval']['label']}" : "none" rescue ""
166
166
  plan_query_string = "new_plan=#{current_plan_string}&old_plan=#{old_plan_string}"
167
167
  if app_slug.present? && plan_page_route_value.present?
168
- redirect_to "https://#{params[:shop]}/admin/apps/#{app_slug}/#{plan_page_route_value}?#{plan_query_string}"
168
+ redirect_to "https://#{params[:shop]}/admin/apps/#{app_slug}/#{plan_page_route_value}?#{plan_query_string}", :allow_other_host => true
169
169
  elsif app_slug.present? && !plan_page_route_value.present?
170
- redirect_to "https://#{params[:shop]}/admin/apps/#{app_slug}"
170
+ redirect_to "https://#{params[:shop]}/admin/apps/#{app_slug}", :allow_other_host => true
171
171
  else
172
- redirect_to "#{app_url}?host=#{embed_host}&shop=#{params[:shop]}", :status => 301 and return
172
+ redirect_to "#{app_url}?host=#{embed_host}&shop=#{params[:shop]}", :allow_other_host => true, :status => 301 and return
173
173
  end
174
174
  else
175
175
  raise Error, "Invalid shopify charge #{charges.inspect}"
@@ -181,9 +181,9 @@ module AppManager
181
181
  if params[:shop].present?
182
182
  embed_host = Base64.encode64(params[:shop] + "/admin")
183
183
  if app_slug.present?
184
- redirect_to "https://#{params[:shop]}/admin/apps/#{app_slug}"
184
+ redirect_to "https://#{params[:shop]}/admin/apps/#{app_slug}", :allow_other_host => true
185
185
  else
186
- redirect_to "#{app_url}?host=#{embed_host}&shop=#{params[:shop]}", :status => 301 and return
186
+ redirect_to "#{app_url}?host=#{embed_host}&shop=#{params[:shop]}", :allow_other_host => true, :status => 301 and return
187
187
  end
188
188
  else
189
189
  raise Error, "Invalid params, must have charge_id,shop && plan in charge controller"
@@ -13,6 +13,8 @@ module AppManager
13
13
 
14
14
 
15
15
  def plans
16
+ most_popular_plan_ids_raw = AppManager.configuration.most_popular_plan_ids || []
17
+ most_popular_plan_ids = Array(most_popular_plan_ids_raw).filter_map { |v| v.to_s =~ /\A\d+\z/ ? v.to_i : nil }.uniq
16
18
  active_plan_id_or_name = nil
17
19
  shopify_plan = nil
18
20
  plan = nil
@@ -96,6 +98,7 @@ module AppManager
96
98
  'bundle_plan' => bundle_plan,
97
99
  'bundle_details' => app_bundle_data,
98
100
  'default_plan_id' => default_plan_id,
101
+ 'most_popular_plan_ids' => most_popular_plan_ids,
99
102
  'choose_later' => choose_later,
100
103
  'has_active_charge' => (((active_charge && active_charge.any? && !active_charge['active_charge'].nil?) or !trial_activated_at) ? true : false),
101
104
  'global_plan_charge' => plan.present? && plan['is_global'] && active_charge.present? && active_charge.any? && !active_charge['active_charge'].nil? && !active_charge['bundle_charge'].nil?
@@ -192,20 +195,50 @@ module AppManager
192
195
 
193
196
  def fail_safe_backup
194
197
  params_permit
198
+ backup_type = params[:backup_type] || "full"
199
+ if (backup_type == "incremental")
200
+ fail_safe_incremental_backup
201
+ else
202
+ rebuild_failsafe
203
+ end
204
+ head :ok
205
+ end
206
+
207
+ def fail_safe_incremental_backup
195
208
  Thread.new do
196
209
  @fs = AppManager::FailSafe.new
197
210
  begin
198
- @fs.sync_app_manager
211
+ @fs.sync_app_manager
199
212
  rescue Exception => e
200
213
  Rails.logger.info "APP MANAGER >>>> LOCAL DB couldn't sync with POTAL DB #{e.inspect}"
201
214
  end
202
215
 
203
216
  begin
204
- @fs.save_api_data(params)
205
- rescue Exception => e
217
+ @fs.fail_safe_incremental_backup(params)
218
+ rescue Exception => e
219
+ Rails.logger.info "APP MANAGER fail_safe_incremental_backup >>>> #{e.inspect}"
220
+ end
221
+ AppManager.clear_cache
222
+ end
223
+ end
224
+
225
+
226
+ def rebuild_failsafe
227
+ params_permit
228
+ Thread.new do
229
+ @fs = AppManager::FailSafe.new
230
+ begin
231
+ @fs.sync_app_manager
232
+ rescue Exception => e
233
+ Rails.logger.info "APP MANAGER >>>> LOCAL DB couldn't sync with POTAL DB #{e.inspect}"
234
+ end
235
+
236
+ begin
237
+ @fs.save_api_data(params)
238
+ rescue Exception => e
206
239
  Rails.logger.info "APP MANAGER >>>> #{e.inspect}"
207
- end
208
- AppManager.clear_cache
240
+ end
241
+ AppManager.clear_cache
209
242
  end
210
243
  head :ok
211
244
  end
data/config/routes.rb CHANGED
@@ -1,23 +1,20 @@
1
1
  AppManager::Engine.routes.draw do
2
2
  scope 'api/app-manager' do
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
- post 'cancel-charge' => 'charges#cancel_charge'
15
- post 'plan/activate-global' => 'charges#activate_global'
16
- post 'plan/cancel-global' => 'charges#cancel_global'
17
-
18
-
19
- scope 'app-manager-api' do
20
- post 'store-charge' => 'plans#store_charge'
21
- end
3
+ get 'marketing-banners' => 'banners#marketing_banner'
4
+ get 'plan-features' => 'plans#index'
5
+ get 'plans' => 'plans#plans'
6
+ get 'users' => 'plans#users'
7
+ post 'active-without-plan' => 'plans#active_without_plan'
8
+ post 'burst-cache' => 'plans#burst_cache'
9
+ post 'fail-safe-backup' => 'plans#fail_safe_backup'
10
+ post 'fail-safe-backup-full' => 'plans#rebuild_failsafe'
11
+ get 'plan/process/:plan_id' => 'charges#process_plan'
12
+ get 'plan/callback' => 'charges#callback', as: :plan_callback
13
+ post 'cancel-charge' => 'charges#cancel_charge'
14
+ post 'plan/activate-global' => 'charges#activate_global'
15
+ post 'plan/cancel-global' => 'charges#cancel_global'
16
+ scope 'app-manager-api' do
17
+ post 'store-charge' => 'plans#store_charge'
18
+ end
22
19
  end
23
20
  end
@@ -14,6 +14,7 @@ module AppManager
14
14
  attr_accessor :field_names
15
15
  attr_accessor :refresh_by_request_params
16
16
  attr_accessor :shopify_app_slug
17
+ attr_accessor :most_popular_plan_ids
17
18
  attr_accessor :plan_page_route
18
19
 
19
20
  def initialize
@@ -28,6 +29,7 @@ module AppManager
28
29
  @plan_id_or_name_field = nil
29
30
  @shopify_app_slug = nil
30
31
  @plan_page_route = nil
32
+ @most_popular_plan_ids = []
31
33
  @field_names = {
32
34
  'name' => 'shopify_domain', # sample example: demo-chirag-parmar.myshopify.com
33
35
  'shopify_email' => 'email', # chirag.p@hulkapps.com
@@ -94,5 +96,8 @@ module AppManager
94
96
  @plan_page_route
95
97
  end
96
98
 
99
+ def most_popular_plan_ids
100
+ @most_popular_plan_ids
101
+ end
97
102
  end
98
103
  end
@@ -110,7 +110,9 @@ module AppManager
110
110
  store_base_plan = plan['store_base_plan'] ? 1 : 0
111
111
  choose_later_plan = plan['choose_later_plan'] ? 1 : 0
112
112
  is_external_charge = plan['is_external_charge'] ? 1 : 0
113
+ if !plan["id"].nil?
113
114
  plan_data << AppManager::Plan.new(plan_id: plan["id"], type: plan["type"], name: plan["name"], price: plan["price"], offer_text: plan["offer_text"], description: plan["description"], interval: interval, shopify_plans: shopify_plans, trial_days: plan["trial_days"], test: plan_test, on_install: plan["on_install"], is_custom: is_custom, app_id: plan["app_id"], base_plan: plan["base_plan"], created_at: plan["created_at"], updated_at: plan["updated_at"], public: public_val, discount: plan["discount"], cycle_count: plan["cycle_count"], store_base_plan: store_base_plan, choose_later_plan: choose_later_plan, discount_type: plan["discount_type"], affiliate: affiliate, features: features, deleted_at: plan["deleted_at"],is_external_charge: plan["is_external_charge"],external_charge_limit: plan["external_charge_limit"],terms: plan["terms"])
115
+ end
114
116
  end
115
117
  AppManager::Plan.bulk_import plan_data
116
118
  end
@@ -128,7 +130,9 @@ module AppManager
128
130
  charge_data = []
129
131
  charges.each do |charge|
130
132
  charge_test = charge['test'] ? true : false
133
+ if !charge["id"].nil?
131
134
  charge_data << AppManager::Charge.new(c_id: charge["id"],charge_id: charge["charge_id"], test: charge_test, status: charge["status"], name: charge["name"], type: charge["type"], price: charge["price"], interval: charge["interval"], trial_days: charge["trial_days"], billing_on: charge["billing_on"], activated_on: charge["activated_on"], trial_ends_on: charge["trial_ends_on"], cancelled_on: charge["cancelled_on"], expires_on: charge["expires_on"], plan_id: charge["plan_id"], description: charge["description"], shop_domain: charge["shop_domain"], created_at: charge["created_at"], updated_at: charge["updated_at"], app_id: charge["app_id"], sync: true)
135
+ end
132
136
  end
133
137
  AppManager::Charge.bulk_import charge_data
134
138
  end
@@ -148,7 +152,7 @@ module AppManager
148
152
  # apps_data << AppManager::App.new(id: app['id'], name: app['name'], slug: app['slug'], url: app['url'], image: app['image'], api_token: app['api_token'], slack: app['slack'], created_at: app['created_at'], updated_at: app['updated_at'])
149
153
  apps_data << AppManager::App.new(app_id: app['id'],name: app['name'], slug: app['slug'], url: app['url'], image: app['image'], api_token: app['api_token'], slack: app['slack'], created_at: app['created_at'], updated_at: app['updated_at'])
150
154
  end
151
- AppManager::Charge.bulk_import apps_data
155
+ AppManager::App.bulk_import apps_data
152
156
  end
153
157
  end
154
158
 
@@ -754,6 +758,249 @@ module AppManager
754
758
  end
755
759
 
756
760
 
761
+
762
+ def standardize_data_format(data)
763
+ case data
764
+ when Array
765
+ data
766
+ when Hash
767
+ [data]
768
+ else
769
+ []
770
+ end
771
+ end
772
+
773
+ def single_record?(data)
774
+ return true if data.is_a?(Hash) && data.key?("id")
775
+ return true if data.respond_to?(:id) && data.id.present?
776
+ false
777
+ end
778
+
779
+ def format_date(value)
780
+ Time.parse(value.to_s).strftime("%Y-%m-%d %H:%M:%S")
781
+ rescue
782
+ value
783
+ end
784
+
785
+ def filter_data(data, date_fields = [], exclude_keys = %w[app_id pivot])
786
+ rows = standardize_data_format(data)
787
+
788
+ processed = rows.map do |row|
789
+ row = row.is_a?(Hash) ? row : {}
790
+ serialize_row_for_failsafe(row, date_fields, exclude_keys)
791
+ end
792
+
793
+ processed.first || {}
794
+ end
795
+
796
+ def serialize_row_for_failsafe(row, date_fields, exclude_keys)
797
+ row.each_with_object({}) do |(key, value), result|
798
+ next if exclude_keys.include?(key.to_s)
799
+
800
+ if date_fields.include?(key.to_s) && value.present?
801
+ result[key] = format_date(value)
802
+ elsif value.is_a?(Hash) || value.is_a?(Array)
803
+ result[key] = value.to_json
804
+ else
805
+ result[key] = value
806
+ end
807
+ end
808
+ end
809
+
810
+ def fail_safe_incremental_backup(params)
811
+ params = params.to_unsafe_h.deep_stringify_keys
812
+
813
+ sync_type = params["sync_type"]
814
+ payload = params["payload"] || {}
815
+
816
+ #initialize_failsafe_db
817
+
818
+ date_fields = %w[
819
+ created_at
820
+ updated_at
821
+ deleted_at
822
+ valid_from
823
+ valid_to
824
+ cancelled_on
825
+ ]
826
+
827
+ case sync_type
828
+
829
+ when "plans"
830
+
831
+ payload["plan_id"] = payload.delete("id")
832
+ payload["test"] ||= 0
833
+
834
+ filtered = filter_data(payload, date_fields)
835
+
836
+ record = AppManager::Plan.find_or_initialize_by(
837
+ plan_id: filtered["plan_id"]
838
+ )
839
+
840
+ record.update!(filtered)
841
+ when "plan-delete"
842
+ AppManager::Plan.where(plan_id: payload["id"]).delete_all
843
+
844
+ when "plan-user-delete"
845
+ AppManager::PlanUser.where(shop_domain: payload["shop_domain"]).delete_all
846
+
847
+ when "charges"
848
+ payload["c_id"] = payload.delete("id")
849
+ filtered = filter_data(payload, date_fields)
850
+
851
+ record = AppManager::Charge.find_or_initialize_by(c_id: filtered["c_id"])
852
+ record.update!(filtered)
853
+
854
+ when "charges-cancel"
855
+ AppManager::Charge
856
+ .where(shop_domain: payload["shop_domain"])
857
+ .update_all(
858
+ status: "cancelled",
859
+ cancelled_on: format_date(payload["cancelled_on"] || Time.current),
860
+ updated_at: format_date(Time.current)
861
+ )
862
+
863
+ when "banners"
864
+ record = AppManager::AppStructure.first_or_initialize
865
+
866
+ record.banners =
867
+ if payload.is_a?(String)
868
+ payload
869
+ else
870
+ payload.to_json
871
+ end
872
+
873
+ record.save!
874
+
875
+ when "promotional-discounts"
876
+
877
+ # Normalize main payload
878
+ payload["discount_id"] = payload.delete("id")
879
+ payload["discount_type"] = payload.delete("type")
880
+
881
+ filtered = filter_data(payload, date_fields, ["pivot"])
882
+
883
+ main_data = filtered.except(
884
+ "shops_relation",
885
+ "apps_relation",
886
+ "plans_relation",
887
+ "usage_relation"
888
+ )
889
+
890
+ discount = AppManager::Discount.find_or_initialize_by(
891
+ discount_id: main_data["discount_id"]
892
+ )
893
+ discount.update!(main_data)
894
+
895
+ # ==========================================================
896
+ # SHOPS RELATION
897
+ # ==========================================================
898
+
899
+ if payload["shops_relation"].present?
900
+
901
+ AppManager::DiscountShop
902
+ .where(discount_id: main_data["discount_id"])
903
+ .delete_all
904
+
905
+ rows = Array(payload["shops_relation"]).map do |row|
906
+ row = row.deep_stringify_keys
907
+
908
+ {
909
+ "discount_id" => main_data["discount_id"],
910
+ "domain" => row["domain"]
911
+ }
912
+ end
913
+
914
+ AppManager::DiscountShop.insert_all(rows) if rows.present?
915
+ end
916
+
917
+ # ==========================================================
918
+ # PLANS RELATION
919
+ # ==========================================================
920
+
921
+ if payload["plans_relation"].present?
922
+
923
+ AppManager::DiscountLinkPlan
924
+ .where(discount_id: main_data["discount_id"])
925
+ .delete_all
926
+
927
+ rows = Array(payload["plans_relation"]).map do |row|
928
+ row = row.deep_stringify_keys
929
+
930
+ {
931
+ "discount_id" => main_data["discount_id"],
932
+ "plan_id" => row["plan_id"]
933
+ }
934
+ end
935
+
936
+ AppManager::DiscountLinkPlan.insert_all(rows) if rows.present?
937
+ end
938
+
939
+ # ==========================================================
940
+ # USAGE RELATION
941
+ # ==========================================================
942
+
943
+ if payload["usage_relation"].present?
944
+
945
+ AppManager::DiscountUsageLog
946
+ .where(discount_id: main_data["discount_id"])
947
+ .delete_all
948
+
949
+ rows = Array(payload["usage_relation"]).map do |row|
950
+ row = row.deep_stringify_keys
951
+ puts "================"
952
+ puts row.inspect
953
+ puts "============="
954
+ {
955
+ "discount_id" => main_data["discount_id"],
956
+ "domain" => row["domain"],
957
+ "created_at" => row["created_at"],
958
+ "updated_at" => row["updated_at"],
959
+ "app_id" => row["app_id"],
960
+ "sync" => row['sync'],
961
+ "process_type" => row['process_type']
962
+ }
963
+ end
964
+
965
+ AppManager::DiscountUsageLog.insert_all(rows) if rows.present?
966
+ end
967
+
968
+ when "promotional-discounts-delete"
969
+ AppManager::Discount
970
+ .where(discount_id: payload["id"])
971
+ .update_all(
972
+ deleted_at: format_date(payload["deleted_at"] || Time.current),
973
+ updated_at: format_date(Time.current)
974
+ )
975
+
976
+ when "plan-discount"
977
+ filtered = filter_data(payload, date_fields)
978
+
979
+ record = AppManager::DiscountPlan.find_or_initialize_by(
980
+ discount_plan_id: filtered["discount_plan_id"]
981
+ )
982
+ record.update!(filtered)
983
+
984
+ when "plan-user"
985
+ filtered = filter_data(payload, date_fields)
986
+
987
+ record = AppManager::PlanUser.find_or_initialize_by(
988
+ plan_user_id: filtered["plan_user_id"]
989
+ )
990
+ record.update!(filtered)
991
+
992
+ when "extend-trial"
993
+ filtered = filter_data(payload, date_fields)
994
+
995
+ record = AppManager::ExtendTrial.find_or_initialize_by(
996
+ extend_trial_id: filtered["extend_trial_id"]
997
+ )
998
+ record.update!(filtered)
999
+
1000
+ else
1001
+ Rails.logger.error("Failsafe: Unhandled sync type: #{sync_type}")
1002
+ end
1003
+ end
757
1004
  end
758
1005
 
759
1006
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppManager
4
- VERSION = "2.3.6"
4
+ VERSION = "2.4.0"
5
5
  end
@@ -8,6 +8,7 @@ AppManager.configure do |config|
8
8
  config.shopify_domain_field = 'shopify_domain' #shopify domain field
9
9
  config.plan_id_or_name_field = 'plan_id'
10
10
  config.shopify_app_slug = '' #Add your app slug here
11
+ config.most_popular_plan_ids = [] #Add your app slug here
11
12
  config.field_names = {
12
13
  'name' => 'shopify_domain', # demo-rahul-tiwari.myshopify.com
13
14
  'shopify_email' => 'email', # rahul.t@hulkapps.com
@@ -17,6 +18,7 @@ AppManager.configure do |config|
17
18
  'created_at' => 'created_at', # 2022-04-15 10:43:05
18
19
  'trial_activated_at' => 'trial_activated_at', # field name that stores trial start/activated date
19
20
  'grandfathered' => 'grandfathered',
21
+ 'partner_development' => 'partner_development',
20
22
  'total_trial_days' => '' #optional, put a trial days field from your shops table otherwise leave it blank
21
23
  }
22
24
  config.plan_features = [
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.6
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rahul Tiwari @ Hulkapps
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-28 00:00:00.000000000 Z
11
+ date: 2026-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty