workarea-core 3.5.0.beta.1 → 3.5.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/workarea/current_tracking.rb +4 -0
  3. data/app/models/workarea/checkout/collect_payment.rb +5 -5
  4. data/app/models/workarea/data_file/operation.rb +4 -0
  5. data/app/models/workarea/fulfillment/policies/base.rb +0 -4
  6. data/app/models/workarea/fulfillment/policies/{ignore.rb → shipping.rb} +2 -2
  7. data/app/models/workarea/fulfillment/sku.rb +0 -2
  8. data/app/models/workarea/insights/trending_searches.rb +8 -1
  9. data/app/models/workarea/metrics/search_by_day.rb +2 -0
  10. data/app/models/workarea/order.rb +12 -2
  11. data/app/models/workarea/order/item.rb +28 -2
  12. data/app/models/workarea/pricing/calculators/tax_calculator.rb +2 -2
  13. data/app/models/workarea/pricing/discount.rb +3 -2
  14. data/app/models/workarea/releasable.rb +3 -5
  15. data/app/models/workarea/reports/custom_event.rb +22 -0
  16. data/app/models/workarea/search/admin/releasable.rb +18 -6
  17. data/app/models/workarea/segment/life_cycle.rb +5 -5
  18. data/app/models/workarea/segment/rules/last_order.rb +9 -2
  19. data/app/models/workarea/segment/rules/traffic_referrer.rb +16 -5
  20. data/app/models/workarea/segmentable.rb +13 -0
  21. data/app/models/workarea/traffic_referrer.rb +3 -0
  22. data/app/queries/workarea/alerts.rb +21 -0
  23. data/app/queries/workarea/order_item_details.rb +16 -16
  24. data/app/queries/workarea/search/admin_index_search.rb +2 -1
  25. data/app/queries/workarea/search/product_display_rules.rb +0 -2
  26. data/app/services/workarea/direct_upload.rb +5 -0
  27. data/app/services/workarea/packaging.rb +1 -1
  28. data/app/workers/workarea/deactivate_stale_discounts.rb +1 -1
  29. data/app/workers/workarea/synchronize_user_metrics.rb +9 -0
  30. data/config/locales/en.yml +22 -0
  31. data/lib/tasks/migrate.rake +9 -12
  32. data/lib/workarea/configuration.rb +6 -6
  33. data/lib/workarea/configuration/redis.rb +21 -3
  34. data/lib/workarea/core.rb +3 -0
  35. data/lib/workarea/ext/freedom_patches/referer_parser.rb +7 -0
  36. data/lib/workarea/ext/mongoid/embedded_children.rb +20 -0
  37. data/lib/workarea/latest_version.rb +24 -0
  38. data/lib/workarea/ping_home_base.rb +0 -1
  39. data/lib/workarea/version.rb +1 -1
  40. data/lib/workarea/visit.rb +5 -2
  41. data/lib/workarea/warnings.rb +6 -6
  42. data/test/lib/workarea/ext/mongoid/embedded_children_test.rb +32 -0
  43. data/test/lib/workarea/latest_version_test.rb +11 -0
  44. data/test/models/workarea/checkout/collect_payment_test.rb +6 -6
  45. data/test/models/workarea/data_file/csv_test.rb +15 -0
  46. data/test/models/workarea/fulfillment/sku_test.rb +5 -5
  47. data/test/models/workarea/insights/cold_searches_test.rb +13 -11
  48. data/test/models/workarea/insights/hot_searches_test.rb +13 -11
  49. data/test/models/workarea/insights/searches_to_improve_test.rb +9 -6
  50. data/test/models/workarea/insights/star_searches_test.rb +5 -4
  51. data/test/models/workarea/insights/trending_searches_test.rb +12 -9
  52. data/test/models/workarea/pricing/calculators/tax_calculator_test.rb +1 -1
  53. data/test/models/workarea/search/admin/releasable_test.rb +5 -7
  54. data/test/models/workarea/segment/life_cycle_test.rb +5 -0
  55. data/test/models/workarea/segment/rules/last_order_test.rb +15 -3
  56. data/test/models/workarea/segment/rules/traffic_referrer_test.rb +10 -8
  57. data/test/models/workarea/segmentable_test.rb +18 -0
  58. data/test/queries/workarea/alerts_test.rb +11 -0
  59. data/test/queries/workarea/order_item_details_test.rb +4 -12
  60. data/test/services/workarea/direct_upload_test.rb +3 -0
  61. data/test/vcr_cassettes/get_latest_version.yml +90 -0
  62. data/test/workers/workarea/deactivate_stale_discounts_test.rb +2 -2
  63. data/workarea-core.gemspec +2 -3
  64. metadata +16 -25
  65. data/app/controllers/workarea/current_referrer.rb +0 -14
  66. data/app/models/workarea/fulfillment/policies/ship.rb +0 -15
@@ -45,24 +45,24 @@ module Workarea
45
45
  @fulfillment ||= Fulfillment::Sku.find_or_initialize_by(id: sku)
46
46
  end
47
47
 
48
- # This is a stop-gap for transitioning away from a digital flag for
49
- # determining checkout behavior for items not requiring
50
- # physical fulfillment.
51
- #
52
- # TODO: remove #digital? usage in v3.6
53
- #
54
- def requires_shipping?
55
- !product.digital? && fulfillment.requires_shipping?
48
+ def to_h
49
+ Rails.cache.fetch(cache_key, expires_in: cache_expiration) do
50
+ {
51
+ product_id: product.id,
52
+ product_attributes: product.as_document,
53
+ category_ids: category_ids,
54
+ discountable: pricing.discountable?,
55
+ fulfillment: fulfillment.policy
56
+ }
57
+ end
56
58
  end
57
59
 
58
- def to_h
59
- {
60
- product_id: product.id,
61
- product_attributes: product.as_document,
62
- category_ids: category_ids,
63
- discountable: pricing.discountable?,
64
- requires_shipping: requires_shipping?
65
- }
60
+ def cache_key
61
+ "order_item_details/#{product.cache_key}/#{sku}"
62
+ end
63
+
64
+ def cache_expiration
65
+ Workarea.config.cache_expirations.order_item_details
66
66
  end
67
67
  end
68
68
  end
@@ -119,7 +119,8 @@ module Workarea
119
119
  TermsFacet.new(self, 'type'),
120
120
  TermsFacet.new(self, 'status'),
121
121
  TermsFacet.new(self, 'tags'),
122
- TermsFacet.new(self, 'upcoming_changes')
122
+ TermsFacet.new(self, 'upcoming_changes'),
123
+ TermsFacet.new(self, 'active_by_segment')
123
124
  ]
124
125
  end
125
126
  end
@@ -25,8 +25,6 @@ module Workarea
25
25
  }
26
26
  end
27
27
 
28
- private
29
-
30
28
  def inventory_display_clause(allow_displayable_when_out_of_stock: true)
31
29
  result = { bool: { should: [{ range: { 'numeric.inventory': { gt: 0 } } }] } }
32
30
 
@@ -7,6 +7,9 @@ module Workarea
7
7
  url = "#{uri.scheme}://#{uri.host}"
8
8
  url += ":#{uri.port}" unless uri.port.in? [80, 443]
9
9
 
10
+ redis_key = "cors_#{url.optionize}"
11
+ return if Workarea.redis.get(redis_key) == 'true'
12
+
10
13
  Workarea.s3.put_bucket_cors(
11
14
  Configuration::S3.bucket,
12
15
  'CORSConfiguration' => [
@@ -18,6 +21,8 @@ module Workarea
18
21
  }
19
22
  ]
20
23
  )
24
+
25
+ Workarea.redis.set(redis_key, 'true')
21
26
  end
22
27
 
23
28
  attr_reader :type, :filename
@@ -89,7 +89,7 @@ module Workarea
89
89
  end
90
90
 
91
91
  def shippable_items
92
- @order.items.select(&:requires_shipping?)
92
+ @order.items.select(&:shipping?)
93
93
  end
94
94
 
95
95
  def stacked_dimensions
@@ -29,7 +29,7 @@ module Workarea
29
29
 
30
30
  def all_active_discount_ids
31
31
  Pricing::Discount
32
- .where(:created_at.lt => Workarea.config.discount_staleness_ttl.ago)
32
+ .where(:updated_at.lt => Workarea.config.discount_staleness_ttl.ago)
33
33
  .select(&:active?)
34
34
  .map(&:id)
35
35
  .map(&:to_s)
@@ -8,6 +8,15 @@ module Workarea
8
8
  queue: 'low'
9
9
  )
10
10
 
11
+ # It's essential for the {Metrics::User#admin} field always be in sync, so
12
+ # we always want this worker enabled.
13
+ #
14
+ # @return [Boolean]
15
+ #
16
+ def self.enabled?
17
+ true
18
+ end
19
+
11
20
  def perform(id)
12
21
  user = User.find(id)
13
22
  metrics = Metrics::User.find_or_create_by(id: user.email)
@@ -50,6 +50,28 @@ en:
50
50
  unit: instance
51
51
  csv:
52
52
  unit: row
53
+ duration:
54
+ years:
55
+ one: '%{count} year'
56
+ other: '%{count} years'
57
+ months:
58
+ one: '%{count} month'
59
+ other: '%{count} months'
60
+ weeks:
61
+ one: '%{count} week'
62
+ other: '%{count} weeks'
63
+ days:
64
+ one: '%{count} day'
65
+ other: '%{count} days'
66
+ hours:
67
+ one: '%{count} hour'
68
+ other: '%{count} hours'
69
+ minutes:
70
+ one: '%{count} minute'
71
+ other: '%{count} minutes'
72
+ seconds:
73
+ one: '%{count} second'
74
+ other: '%{count} seconds'
53
75
  errors:
54
76
  messages:
55
77
  contains_type: cannot contain 'type'
@@ -35,18 +35,6 @@ namespace :workarea do
35
35
 
36
36
  puts "✅ #{count} tax categories updated."
37
37
 
38
- Workarea::Catalog::Product.each_by(500) do |product|
39
- next unless product.digital?
40
-
41
- product.skus.each do |sku|
42
- fulfillment = Workarea::Fulfillment::Sku.find_or_initialize_by(id: sku)
43
- fulfillment.policy = 'ignore'
44
- fulfillment.save!
45
- end
46
- end
47
-
48
- puts "✅ #{count} fulfillment skus for digital product have been created."
49
-
50
38
  count = 0
51
39
  failed_ids = []
52
40
  backup = Mongo::Collection.new(Mongoid::Clients.default.database, 'workarea_legacy_segments')
@@ -100,6 +88,15 @@ namespace :workarea do
100
88
  puts "The segments that failed are #{failed_ids.to_sentence}."
101
89
  end
102
90
 
91
+ Workarea::Segment::LifeCycle.create!
92
+ puts "✅ Life cycle segments have been created."
93
+
94
+ admin_ids = Workarea::User.admins.pluck(:id)
95
+ admin_ids.each do |id|
96
+ Workarea::SynchronizeUserMetrics.new.perform(id)
97
+ end
98
+ puts "✅ #{admin_ids.count} admins have had their metrics synchronized." if admin_ids.count > 0
99
+
103
100
  puts "\nMigration complete!"
104
101
  end
105
102
  end
@@ -821,8 +821,7 @@ module Workarea
821
821
  # recommended as the default policy.
822
822
  config.fulfillment_policies = SwappableList.new(
823
823
  %w(
824
- Workarea::Fulfillment::Policies::Ship
825
- Workarea::Fulfillment::Policies::Ignore
824
+ Workarea::Fulfillment::Policies::Shipping
826
825
  Workarea::Fulfillment::Policies::Download
827
826
  )
828
827
  )
@@ -877,6 +876,7 @@ module Workarea
877
876
  config.cache_expirations.sitemap_fragment_cache = 1.day
878
877
  config.cache_expirations.free_gift_attributes = 1.hour
879
878
  config.cache_expirations.reports = 1.hour
879
+ config.cache_expirations.order_item_details = 15.minutes
880
880
 
881
881
  # Send transactional emails. Allows default transaction emails to be
882
882
  # disabled when using third-party email services.
@@ -1257,14 +1257,14 @@ module Workarea
1257
1257
  # setting to configure the `:encoding` options for `CSV.foreach`
1258
1258
  # if your CSV files are failing to import with a UTF-8 encoding
1259
1259
  # error.
1260
- config.csv_import_options = {}
1260
+ config.csv_import_options = { encoding: 'bom|utf-8' }
1261
1261
 
1262
1262
  # Determines what payment action happens when "place order" is submitted.
1263
1263
  # These correspond to methods on the {Checkout}'s instance of {Payment}
1264
1264
  config.checkout_payment_action = {
1265
- shipped: 'authorize!',
1266
- not_shipped: 'purchase!',
1267
- mixed: 'purchase!'
1265
+ shipping: 'authorize!',
1266
+ partial_shipping: 'purchase!',
1267
+ no_shipping: 'purchase!'
1268
1268
  }
1269
1269
 
1270
1270
  # Class used to determine if an order is fraudlent
@@ -1,7 +1,7 @@
1
1
  module Workarea
2
2
  module Configuration
3
3
  class Redis
4
- DEFAULT = { host: 'localhost', port: 6379, db: 0 }.freeze
4
+ DEFAULT = { host: 'localhost', port: 6379, db: 0, scheme: 'redis' }.freeze
5
5
 
6
6
  class << self
7
7
  # Used for Sidekiq and Predictor
@@ -39,11 +39,15 @@ module Workarea
39
39
  return from_config if from_config.present?
40
40
 
41
41
  env_slug = name.to_s.underscore.upcase
42
+ scheme = ENV["WORKAREA_#{env_slug}_SCHEME"].presence || DEFAULT[:scheme]
42
43
 
43
44
  {
45
+ scheme: scheme,
44
46
  host: ENV["WORKAREA_#{env_slug}_HOST"].presence || DEFAULT[:host],
45
47
  port: ENV["WORKAREA_#{env_slug}_PORT"].presence || DEFAULT[:port],
46
- db: ENV["WORKAREA_#{env_slug}_DB"].presence || DEFAULT[:db]
48
+ db: ENV["WORKAREA_#{env_slug}_DB"].presence || DEFAULT[:db],
49
+ password: ENV["WORKAREA_#{env_slug}_PASSWORD"].presence,
50
+ ssl: scheme == 'rediss' ? true : false
47
51
  }
48
52
  end
49
53
  end
@@ -55,10 +59,22 @@ module Workarea
55
59
  @config = config.to_h.deep_symbolize_keys
56
60
  end
57
61
 
62
+ def scheme
63
+ @config[:scheme]
64
+ end
65
+
66
+ def ssl
67
+ @config[:ssl]
68
+ end
69
+
58
70
  def host
59
71
  @config[:host]
60
72
  end
61
73
 
74
+ def password
75
+ @config[:password]
76
+ end
77
+
62
78
  def port
63
79
  @config[:port]
64
80
  end
@@ -68,7 +84,9 @@ module Workarea
68
84
  end
69
85
 
70
86
  def to_url
71
- base = "redis://#{host}"
87
+ base = "#{scheme}://"
88
+ base << "admin:#{password}@" if password.present?
89
+ base << "#{host}"
72
90
  base << ":#{port}" if port.present?
73
91
  base << "/#{db}" if db.present?
74
92
  base
@@ -131,6 +131,7 @@ require 'workarea/ext/freedom_patches/dragonfly_job_fetch_url'
131
131
  require 'workarea/ext/freedom_patches/dragonfly_callable_url_host'
132
132
  require 'workarea/ext/freedom_patches/active_support_duration'
133
133
  require 'workarea/ext/freedom_patches/premailer'
134
+ require 'workarea/ext/freedom_patches/referer_parser'
134
135
  require 'workarea/ext/mongoid/list_field'
135
136
  require 'workarea/ext/mongoid/each_by'
136
137
  require 'workarea/ext/mongoid/except'
@@ -141,6 +142,7 @@ require 'workarea/ext/mongoid/lookup_hash'
141
142
  require 'workarea/ext/active_shipping/workarea'
142
143
  require 'workarea/ext/mongoid/audit_log_entry.decorator'
143
144
  require 'workarea/ext/mongoid/find_ordered'
145
+ require 'workarea/ext/mongoid/embedded_children'
144
146
  require 'workarea/ext/sprockets/ruby_processor'
145
147
  require 'workarea/ext/jbuilder/jbuilder_append_partials'
146
148
 
@@ -218,6 +220,7 @@ require 'workarea/string_id'
218
220
  require 'workarea/mail_interceptor'
219
221
  require 'workarea/visit'
220
222
  require 'workarea/warnings'
223
+ require 'workarea/latest_version'
221
224
 
222
225
  #
223
226
  # Core
@@ -0,0 +1,7 @@
1
+ module RefererParser
2
+ class Parser
3
+ def sources
4
+ @name_hash.values.map { |v| v[:source] }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ module Mongoid
2
+ module Document
3
+ # Returns all the embedded children in this document by recursion.
4
+ #
5
+ # @return [Array<Mongoid::Document>]
6
+ #
7
+ def embedded_children
8
+ result = []
9
+
10
+ embedded_relations.each do |name, metadata|
11
+ Array.wrap(send(name)).each do |child|
12
+ result << child
13
+ result += child.embedded_children
14
+ end
15
+ end
16
+
17
+ result
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Workarea
2
+ module LatestVersion
3
+ def self.get
4
+ Rails.cache.fetch('workarea/version/latest', expires_in: 3.days) do
5
+ request = Net::HTTP::Get.new('/api/v1/gems/workarea.json')
6
+ request.content_type = 'application/json'
7
+
8
+ uri = URI('https://rubygems.org')
9
+ http = Net::HTTP.new(uri.host, uri.port)
10
+ http.use_ssl = true
11
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
12
+ response = http.start { |h| h.request(request) }
13
+
14
+ JSON.parse(response.body)['version']
15
+ end
16
+ rescue Exception => e
17
+ Rails.logger.error '-------------------------------------'
18
+ Rails.logger.error "There was an error contacting rubygems.org!"
19
+ Rails.logger.error e.class
20
+ Rails.logger.error e.message
21
+ Rails.logger.error '-------------------------------------'
22
+ end
23
+ end
24
+ end
@@ -6,7 +6,6 @@ module Workarea
6
6
  class << self
7
7
  def ping
8
8
  begin
9
- ENV['HTTP_PROXY'] || ENV['HTTPS_PROXY']
10
9
  request = Net::HTTP::Post.new('/ping')
11
10
  request['X-WeblincClientName'] = Workarea.config.site_name
12
11
  request['X-WeblincAuthToken'] = auth_token
@@ -3,7 +3,7 @@ module Workarea
3
3
  MAJOR = 3
4
4
  MINOR = 5
5
5
  PATCH = 0
6
- PRE = 'beta.1'
6
+ PRE = nil
7
7
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
8
8
 
9
9
  module MONGODB
@@ -64,8 +64,11 @@ module Workarea
64
64
  end
65
65
 
66
66
  def referrer
67
- return @referrer if defined?(@referrer)
68
- @referrer = Workarea.referrer_parser.parse(request.referrer) rescue {}
67
+ @referrer ||= begin
68
+ value = cookies['workarea_referrer'].presence || request.referrer
69
+ attributes = Workarea.referrer_parser.parse(value) rescue {}
70
+ TrafficReferrer.new(attributes)
71
+ end
69
72
  end
70
73
 
71
74
  def browser
@@ -75,16 +75,16 @@ of items in the order.
75
75
  You should set Workarea.config.checkout_payment_action instead. You can set it
76
76
  for each type of order, here are the defaults:
77
77
  {
78
- shipped: 'authorize!',
79
- not_shipped: 'purchase!',
80
- mixed: 'authorize!'
78
+ shipping: 'authorize!',
79
+ partial_shipping: 'authorize!',
80
+ no_shipping: 'purchase!'
81
81
  }
82
82
 
83
83
  To achieve the same functionality as Workarea.config.auto_capture, you'd set:
84
84
  {
85
- shipped: 'purchase!',
86
- not_shipped: 'purchase!',
87
- mixed: 'purchase!'
85
+ shipping: 'purchase!',
86
+ partial_shipping: 'purchase!',
87
+ no_shipping: 'purchase!'
88
88
  }
89
89
 
90
90
  **************************************************
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ module Mongoid
4
+ class EmbeddedChildrenTest < Workarea::TestCase
5
+ class Parent
6
+ include Mongoid::Document
7
+ embeds_one :child, class_name: 'Mongoid::EmbeddedChildrenTest::Child'
8
+ embeds_many :children, class_name: 'Mongoid::EmbeddedChildrenTest::Child'
9
+ end
10
+
11
+ class Child
12
+ include Mongoid::Document
13
+ field :name, type: String
14
+ embeds_one :grandchild, class_name: 'Mongoid::EmbeddedChildrenTest::Grandchild'
15
+ embeds_many :grandchildren, class_name: 'Mongoid::EmbeddedChildrenTest::Grandchild'
16
+ end
17
+
18
+ class Grandchild
19
+ include Mongoid::Document
20
+ field :name, type: String
21
+ end
22
+
23
+ def test_embedded_children
24
+ model = Parent.create!(
25
+ child: { name: '1', grandchild: { name: '2' } },
26
+ children: [{ name: '3' }, { name: '4', grandchildren: [{ name: '5' }] }]
27
+ )
28
+
29
+ assert_equal(%w(1 2 3 4 5), model.embedded_children.map(&:name))
30
+ end
31
+ end
32
+ end