workarea-core 3.5.7 → 3.5.12

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/workarea/application_controller.rb +0 -5
  3. data/app/controllers/workarea/authentication.rb +6 -0
  4. data/app/helpers/workarea/i18n_helper.rb +1 -1
  5. data/app/middleware/workarea/application_middleware.rb +18 -0
  6. data/app/models/workarea/bulk_action/product_edit.rb +6 -6
  7. data/app/models/workarea/order.rb +1 -1
  8. data/app/models/workarea/search/admin.rb +11 -1
  9. data/app/models/workarea/search/storefront.rb +5 -1
  10. data/app/models/workarea/search/storefront/category_query.rb +2 -2
  11. data/app/models/workarea/search/storefront/product.rb +8 -0
  12. data/app/models/workarea/user/password_reset.rb +3 -1
  13. data/app/queries/workarea/search/pagination.rb +4 -1
  14. data/app/queries/workarea/search/product_entries.rb +7 -5
  15. data/app/queries/workarea/search/storefront_search.rb +1 -1
  16. data/app/services/workarea/direct_upload.rb +12 -13
  17. data/app/services/workarea/hash_update.rb +15 -1
  18. data/app/workers/workarea/bulk_index_admin.rb +1 -1
  19. data/app/workers/workarea/bulk_index_products.rb +3 -2
  20. data/app/workers/workarea/bulk_index_searches.rb +4 -4
  21. data/app/workers/workarea/process_import.rb +3 -3
  22. data/app/workers/workarea/synchronize_user_metrics.rb +12 -2
  23. data/config/initializers/15_endpoint_monitoring.rb +6 -3
  24. data/lib/workarea/cache.rb +1 -1
  25. data/lib/workarea/changelog.rake +1 -1
  26. data/lib/workarea/core.rb +2 -0
  27. data/lib/workarea/core/engine.rb +7 -0
  28. data/lib/workarea/elasticsearch/document.rb +15 -8
  29. data/lib/workarea/ext/freedom_patches/i18n_js.rb +27 -0
  30. data/lib/workarea/ext/freedom_patches/mongoid_localized_defaults.rb +25 -0
  31. data/lib/workarea/geolocation.rb +1 -9
  32. data/lib/workarea/version.rb +1 -1
  33. data/lib/workarea/visit.rb +8 -1
  34. data/lib/workarea/warnings.rb +3 -4
  35. data/test/helpers/workarea/i18n_helper_test.rb +2 -0
  36. data/test/integration/workarea/authentication_test.rb +10 -0
  37. data/test/integration/workarea/cache_varies_integration_test.rb +31 -0
  38. data/test/integration/workarea/monitoring_integration_test.rb +10 -5
  39. data/test/lib/workarea/elasticsearch/document_test.rb +20 -0
  40. data/test/lib/workarea/ext/freedom_patches/mongoid_localized_defaults_test.rb +25 -0
  41. data/test/lib/workarea/geolocation_test.rb +3 -3
  42. data/test/models/workarea/search/storefront_test.rb +15 -0
  43. data/test/models/workarea/segment/rules/geolocation_test.rb +9 -7
  44. data/test/models/workarea/user/password_reset_test.rb +12 -4
  45. data/test/queries/workarea/search/pagination_test.rb +9 -0
  46. data/test/queries/workarea/search/product_entries_test.rb +11 -0
  47. data/test/queries/workarea/search/product_search_test.rb +31 -0
  48. data/test/services/workarea/direct_upload_test.rb +0 -3
  49. data/test/services/workarea/hash_update_test.rb +12 -12
  50. data/test/workers/workarea/process_import_test.rb +6 -0
  51. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26dc982f98d5ae00a8d3299b322cd6eb897329888c169a7a035e0bafad3be1e8
4
- data.tar.gz: f3b5ae6b3bddb68b0e3d5b5c303e565e69ff812e165371e35285cd3ce04d334e
3
+ metadata.gz: 07f92725a7deefad82f4df41734ec888981896e7ed23948b809da43495f3234c
4
+ data.tar.gz: be8b5ee9228c3c3c073f2e0456d2dcb60abb77bae48f904f75b608b6e5ce3fb3
5
5
  SHA512:
6
- metadata.gz: 31ad3ba0206fb6c815e6af2d4af9b572dbefec26460adfa14191279f9bd8098c462eb21b61139a0003ba775ae0b6e65294745b312ba776cd44cb55931221d40f
7
- data.tar.gz: 8ffe28be251fddeba16e2f4964ca716ea8f759d171154529414e3b826accc1a709acb15f0374916d4ff2260f64c21acdd1a44b71687bb93fadec4bc626e81e11
6
+ metadata.gz: 252398a30da73f5e643367f8c63628a7cbbd739d5ec7bf945ffa71d59065b7b7b60b22cf2a9867f96a0e7047136a4b7ff3bddb7e0943fd8c388c88458645d82b
7
+ data.tar.gz: 29fbc57881073fabc8294289477c1ff548f640c10dba07aa8c1409dd2757ff9b78c3ed34236efc22ee99c8c2c9e06006363eb2bb8ca7ba43fd422418e163084f
@@ -7,7 +7,6 @@ module Workarea
7
7
 
8
8
  protect_from_forgery
9
9
 
10
- before_action :set_locale
11
10
  after_action :set_flash_header
12
11
 
13
12
  # Cache templates within the scope of a request for development
@@ -33,10 +32,6 @@ module Workarea
33
32
 
34
33
  private
35
34
 
36
- def set_locale
37
- I18n.locale = params[:locale] || I18n.default_locale
38
- end
39
-
40
35
  def set_flash_header
41
36
  messages = flash.map { |k, v| [k, ERB::Util.h(v)] }
42
37
  response.headers['X-Flash-Messages'] = Hash[messages].to_json
@@ -89,6 +89,12 @@ module Workarea
89
89
  else
90
90
  uri = URI.parse(params[:return_to])
91
91
 
92
+ if I18n.locale != I18n.default_locale
93
+ query_hash = Rack::Utils.parse_nested_query(uri.query)
94
+ query_hash['locale'] ||= I18n.locale
95
+ uri.query = query_hash.to_query
96
+ end
97
+
92
98
  if uri.fragment.present?
93
99
  "#{uri.request_uri}##{uri.fragment}"
94
100
  else
@@ -10,7 +10,7 @@ module Workarea
10
10
  result = ''
11
11
 
12
12
  params
13
- .except(:utf8, :controller, :action)
13
+ .except(:utf8, :controller, :action, :locale)
14
14
  .each_pair { |key, value| result << hidden_field_tag(key, value, id: nil) }
15
15
 
16
16
  result.html_safe
@@ -8,6 +8,7 @@ module Workarea
8
8
  request = Rack::Request.new(env)
9
9
  return @app.call(env) if request.path =~ /(jpe?g|png|ico|gif|css|js|svg)$/
10
10
 
11
+ set_locale(env, request)
11
12
  setup_environment(env, request)
12
13
  set_segment_request_headers(env)
13
14
  status, headers, body = @app.call(env)
@@ -16,6 +17,10 @@ module Workarea
16
17
  [status, headers, body]
17
18
  end
18
19
 
20
+ def set_locale(env, request)
21
+ I18n.locale = locale_from_request(env, request) || I18n.default_locale
22
+ end
23
+
19
24
  def setup_environment(env, request)
20
25
  env['workarea.visit'] = Visit.new(env)
21
26
  env['workarea.cache_varies'] = Cache::Varies.new(env['workarea.visit']).to_s
@@ -37,6 +42,19 @@ module Workarea
37
42
 
38
43
  private
39
44
 
45
+ def locale_from_request(env, request)
46
+ return request.params['locale'] if request.params['locale'].present?
47
+
48
+ env_with_method = env.merge(
49
+ method: request.params[Rack::MethodOverride::METHOD_OVERRIDE_PARAM_KEY].presence ||
50
+ request.request_method
51
+ )
52
+ Rails.application.routes.recognize_path(request.path, env_with_method)[:locale]
53
+
54
+ rescue ActionController::RoutingError
55
+ # Return nil since we can't get locale out of this request
56
+ end
57
+
40
58
  def normalize_segment_ids(visit)
41
59
  visit.applied_segments.map(&:id).sort.join(',')
42
60
  end
@@ -36,15 +36,15 @@ module Workarea
36
36
  end
37
37
 
38
38
  def apply_details!(product)
39
- HashUpdate
40
- .new(adds: add_details, removes: remove_details)
41
- .apply(product.details)
39
+ product.details = HashUpdate
40
+ .new(original: product.details, adds: add_details, removes: remove_details)
41
+ .result
42
42
  end
43
43
 
44
44
  def apply_filters!(product)
45
- HashUpdate
46
- .new(adds: add_filters, removes: remove_filters)
47
- .apply(product.filters)
45
+ product.filters = HashUpdate
46
+ .new(original: product.filters, adds: add_filters, removes: remove_filters)
47
+ .result
48
48
  end
49
49
 
50
50
  def apply_pricing!(product)
@@ -53,7 +53,7 @@ module Workarea
53
53
  fraud_suspected_at: 1
54
54
  },
55
55
  {
56
- name: 'abandoned_order_email_index',
56
+ name: 'abandoned_order_email_with_fraud_index',
57
57
  background: true
58
58
  }
59
59
  )
@@ -18,7 +18,17 @@ module Workarea
18
18
  aggs: {
19
19
  type: {
20
20
  terms: { field: 'facets.type', size: Workarea.config.jump_to_type_limit },
21
- aggs: { top: { top_hits: { size: Workarea.config.jump_to_results_per_type } } }
21
+ aggs: {
22
+ top: {
23
+ top_hits: {
24
+ size: Workarea.config.jump_to_results_per_type,
25
+ sort: [
26
+ { _score: { order: 'desc' } },
27
+ { updated_at: { order: 'desc' } }
28
+ ]
29
+ }
30
+ }
31
+ }
22
32
  }
23
33
  }
24
34
  }
@@ -78,6 +78,10 @@ module Workarea
78
78
  nil
79
79
  end
80
80
 
81
+ def changesets
82
+ @changesets ||= Array.wrap(model.try(:changesets_with_children))
83
+ end
84
+
81
85
  def as_document
82
86
  Release.with_current(release_id) do
83
87
  {
@@ -87,7 +91,7 @@ module Workarea
87
91
  active: active,
88
92
  active_segment_ids: active_segment_ids,
89
93
  release_id: release_id,
90
- changeset_release_ids: Array.wrap(model.try(:changesets)).map(&:release_id),
94
+ changeset_release_ids: changesets.map(&:release_id),
91
95
  suggestion_content: suggestion_content,
92
96
  created_at: model.created_at,
93
97
  updated_at: model.updated_at,
@@ -113,7 +113,7 @@ module Workarea
113
113
  id: category.id,
114
114
  release_id: 'live',
115
115
  changeset_release_ids: changesets.map(&:release_id).uniq,
116
- query: Categorization.new(rules: category.product_rules).query
116
+ query: Workarea::Search::Categorization.new(rules: category.product_rules).query
117
117
  }
118
118
 
119
119
  Storefront.current_index.save(document, type: 'category')
@@ -130,7 +130,7 @@ module Workarea
130
130
  document = {
131
131
  id: "#{category.id}-#{changeset.release_id}",
132
132
  release_id: changeset.release_id,
133
- query: Categorization.new(rules: category.product_rules).query
133
+ query: Workarea::Search::Categorization.new(rules: category.product_rules).query
134
134
  }
135
135
 
136
136
  Storefront.current_index.save(document, type: 'category')
@@ -122,6 +122,14 @@ module Workarea
122
122
  ProductPrimaryImageUrl.new(model).path
123
123
  end
124
124
 
125
+ # Override to include release changesets for pricing, featured products, etc.
126
+ #
127
+ # @return [Mongoid::Criteria]
128
+ #
129
+ def changesets
130
+ @product_changesets ||= ProductReleases.new(model).changesets
131
+ end
132
+
125
133
  private
126
134
 
127
135
  def clean_for_keywords(value)
@@ -4,7 +4,7 @@ module Workarea
4
4
  include ApplicationDocument
5
5
  include UrlToken
6
6
 
7
- belongs_to :user, class_name: 'Workarea::User'
7
+ belongs_to :user, class_name: 'Workarea::User', index: true
8
8
 
9
9
  index(
10
10
  { created_at: 1 },
@@ -14,6 +14,8 @@ module Workarea
14
14
  def self.setup!(email)
15
15
  user = User.find_by_email(email)
16
16
  return nil unless user
17
+
18
+ where(user_id: user.id).destroy_all
17
19
  create!(user: user)
18
20
  end
19
21
 
@@ -7,7 +7,10 @@ module Workarea
7
7
  end
8
8
 
9
9
  def per_page
10
- params[:per_page].presence || Workarea.config.per_page
10
+ return Workarea.config.per_page if params[:per_page].blank?
11
+
12
+ tmp = params[:per_page].to_i
13
+ tmp > 0 ? tmp : Workarea.config.per_page
11
14
  end
12
15
 
13
16
  def size
@@ -17,15 +17,17 @@ module Workarea
17
17
  end
18
18
 
19
19
  def live_entries
20
- @live_entries ||= @products.flat_map do |product|
21
- index_entries_for(product.without_release)
20
+ @live_entries ||= @products.reduce([]) do |memo, product|
21
+ memo + Array.wrap(index_entries_for(product.without_release))
22
22
  end
23
23
  end
24
24
 
25
25
  def release_entries
26
- @release_entries ||= @products.flat_map do |product|
27
- ProductReleases.new(product).releases.map do |release|
28
- index_entries_for(product.in_release(release))
26
+ @release_entries ||= @products.reduce([]) do |results, product|
27
+ releases = ProductReleases.new(product).releases
28
+
29
+ results + releases.reduce([]) do |memo, release|
30
+ memo + Array.wrap(index_entries_for(product.in_release(release)))
29
31
  end
30
32
  end
31
33
  end
@@ -4,7 +4,7 @@ module Workarea
4
4
  attr_reader :params
5
5
 
6
6
  def initialize(params)
7
- @params = params
7
+ @params = params.with_indifferent_access.except(:per_page)
8
8
  @used_middleware = []
9
9
  end
10
10
 
@@ -6,22 +6,21 @@ module Workarea
6
6
  uri = URI.parse(request_url)
7
7
  url = "#{uri.scheme}://#{uri.host}"
8
8
  url += ":#{uri.port}" unless uri.port.in? [80, 443]
9
-
10
- redis_key = "cors_#{url.optionize}"
11
- return if Workarea.redis.get(redis_key) == 'true'
9
+ id = "direct_upload_#{url}"
12
10
 
13
11
  response = Workarea.s3.get_bucket_cors(Configuration::S3.bucket)
14
12
  cors = response.data[:body]
15
- cors['CORSConfiguration'] << {
16
- 'ID' => "direct_upload_#{url}",
17
- 'AllowedMethod' => 'PUT',
18
- 'AllowedOrigin' => url,
19
- 'AllowedHeader' => '*'
20
- }
21
- cors['CORSConfiguration'].uniq!
22
-
23
- Workarea.s3.put_bucket_cors(Configuration::S3.bucket, cors)
24
- Workarea.redis.set(redis_key, 'true')
13
+
14
+ unless cors['CORSConfiguration'].pluck('ID').include?(id)
15
+ cors['CORSConfiguration'] << {
16
+ 'ID' => id,
17
+ 'AllowedMethod' => 'PUT',
18
+ 'AllowedOrigin' => url,
19
+ 'AllowedHeader' => '*'
20
+ }
21
+
22
+ Workarea.s3.put_bucket_cors(Configuration::S3.bucket, cors)
23
+ end
25
24
  end
26
25
 
27
26
  attr_reader :type, :filename
@@ -5,13 +5,27 @@ module Workarea
5
5
  parsed.map(&:to_s).map(&:strip).reject(&:blank?) if parsed.present?
6
6
  end
7
7
 
8
- def initialize(adds: [], updates: [], removes: [])
8
+ def initialize(original: {}, adds: [], updates: [], removes: [])
9
+ @original = original
9
10
  @adds = Array(adds).flatten.each_slice(2).to_a
10
11
  @updates = Array(updates).flatten.each_slice(2).to_a
11
12
  @removes = Array(removes).flatten
12
13
  end
13
14
 
15
+ def result
16
+ apply_to(@original.deep_dup)
17
+ end
18
+
19
+ # TODO v3.6 remove this method, doesn't work when the field is localized
20
+ # @deprecated
14
21
  def apply(hash)
22
+ Workarea.deprecation.deprecate_methods(self.class, apply: :result)
23
+ apply_to(hash)
24
+ end
25
+
26
+ private
27
+
28
+ def apply_to(hash)
15
29
  @adds.each do |tuple|
16
30
  key, value = *tuple
17
31
  hash[key] = self.class.parse_values(value)
@@ -13,7 +13,7 @@ module Workarea
13
13
 
14
14
  def perform_by_models(models)
15
15
  return if models.empty?
16
- Workarea::Search::Admin.bulk(documents_for(models))
16
+ Workarea::Search::Admin.bulk { documents_for(models) }
17
17
  end
18
18
 
19
19
  private
@@ -15,8 +15,9 @@ module Workarea
15
15
  return if products.blank?
16
16
  products = Array.wrap(products)
17
17
 
18
- documents = Search::ProductEntries.new(products).map(&:as_bulk_document)
19
- Search::Storefront.bulk(documents)
18
+ Search::Storefront.bulk do
19
+ Search::ProductEntries.new(products).map(&:as_bulk_document)
20
+ end
20
21
 
21
22
  Catalog::Product.in(id: products.map(&:id)).set(last_indexed_at: Time.current)
22
23
  end
@@ -18,11 +18,11 @@ module Workarea
18
18
  end
19
19
 
20
20
  def perform_by_models(searches)
21
- documents = searches.map do |model|
22
- Search::Storefront::Search.new(model).as_bulk_document
21
+ Search::Storefront.bulk do
22
+ searches.map do |model|
23
+ Search::Storefront::Search.new(model).as_bulk_document
24
+ end
23
25
  end
24
-
25
- Search::Storefront.bulk(documents)
26
26
  end
27
27
  end
28
28
 
@@ -17,11 +17,11 @@ module Workarea
17
17
  import.process!
18
18
 
19
19
  ensure
20
- if import.error?
20
+ if import&.error?
21
21
  Admin::DataFileMailer.import_error(id).deliver_now
22
- elsif import.failure?
22
+ elsif import&.failure?
23
23
  Admin::DataFileMailer.import_failure(id).deliver_now
24
- else
24
+ elsif import.present?
25
25
  Admin::DataFileMailer.import(id).deliver_now
26
26
  end
27
27
  end
@@ -19,8 +19,18 @@ module Workarea
19
19
 
20
20
  def perform(id)
21
21
  user = User.find(id)
22
- metrics = Metrics::User.find_or_create_by(id: user.email)
23
- metrics.set(admin: user.admin?, tags: user.tags)
22
+
23
+ Metrics::User.collection.update_one(
24
+ { _id: user.email },
25
+ {
26
+ '$set' => {
27
+ admin: user.admin?,
28
+ tags: user.tags,
29
+ updated_at: Time.current.utc
30
+ }
31
+ },
32
+ upsert: true
33
+ )
24
34
  end
25
35
  end
26
36
  end
@@ -3,7 +3,8 @@
3
3
  #
4
4
  Easymon::Repository.add(
5
5
  'mongodb',
6
- Workarea::Monitoring::MongoidCheck.new
6
+ Workarea::Monitoring::MongoidCheck.new,
7
+ :critical
7
8
  )
8
9
 
9
10
  #
@@ -11,7 +12,8 @@ Easymon::Repository.add(
11
12
  #
12
13
  Easymon::Repository.add(
13
14
  'elasticsearch',
14
- Workarea::Monitoring::ElasticsearchCheck.new
15
+ Workarea::Monitoring::ElasticsearchCheck.new,
16
+ :critical
15
17
  )
16
18
 
17
19
  #
@@ -21,7 +23,8 @@ Easymon::Repository.add(
21
23
  "redis",
22
24
  Easymon::RedisCheck.new(
23
25
  Workarea::Configuration::Redis.persistent.to_h
24
- )
26
+ ),
27
+ :critical
25
28
  )
26
29
 
27
30
  #
@@ -70,7 +70,7 @@ module Workarea
70
70
  end
71
71
 
72
72
  def to_s
73
- @to_s ||= @varies.map { |v| visit.instance_exec(&v).to_s }.join(':')
73
+ @to_s ||= ([I18n.locale] + @varies.map { |v| visit.instance_exec(&v).to_s }).join(':')
74
74
  end
75
75
  end
76
76
  end
@@ -67,7 +67,7 @@ namespace :workarea do
67
67
  message << " #{line.strip}"
68
68
  end
69
69
  end
70
- message << " #{entry[:author]}\n"
70
+ message << "\n #{entry[:author]}\n"
71
71
  end
72
72
 
73
73
  # ensure and append to changelog
@@ -131,6 +131,8 @@ require 'workarea/ext/freedom_patches/dragonfly_callable_url_host'
131
131
  require 'workarea/ext/freedom_patches/active_support_duration'
132
132
  require 'workarea/ext/freedom_patches/premailer'
133
133
  require 'workarea/ext/freedom_patches/referer_parser'
134
+ require 'workarea/ext/freedom_patches/mongoid_localized_defaults'
135
+ require 'workarea/ext/freedom_patches/i18n_js'
134
136
  require 'workarea/ext/mongoid/list_field'
135
137
  require 'workarea/ext/mongoid/each_by'
136
138
  require 'workarea/ext/mongoid/except'
@@ -72,6 +72,13 @@ module Workarea
72
72
  Workarea::Warnings.check
73
73
  Configuration::Session.validate!
74
74
  end
75
+
76
+ config.to_prepare do
77
+ # For some reason, app/workers/workarea/bulk_index_products.rb doesn't
78
+ # get autoloaded. Without this, admin actions like updating product
79
+ # attributes raises a {NameError} "uninitialized constant BulkIndexProducts".
80
+ require_dependency 'workarea/bulk_index_products'
81
+ end
75
82
  end
76
83
  end
77
84
  end
@@ -44,22 +44,27 @@ module Workarea
44
44
 
45
45
  def save(document, options = {})
46
46
  options = options.merge(type: type)
47
- I18n.for_each_locale { current_index.save(document, options) }
47
+ current_index.save(document, options)
48
48
  end
49
49
 
50
- def bulk(documents, options = {})
50
+ def bulk(documents = [], options = {})
51
51
  options = options.merge(type: type)
52
- I18n.for_each_locale { current_index.bulk(documents, options) }
52
+
53
+ if block_given?
54
+ I18n.for_each_locale { current_index.bulk(Array.wrap(yield), options) }
55
+ else
56
+ current_index.bulk(documents, options)
57
+ end
53
58
  end
54
59
 
55
60
  def update(document, options = {})
56
61
  options = options.merge(type: type)
57
- I18n.for_each_locale { current_index.update(document, options) }
62
+ current_index.update(document, options)
58
63
  end
59
64
 
60
65
  def delete(id, options = {})
61
66
  options = options.merge(type: type)
62
- I18n.for_each_locale { current_index.delete(id, options) }
67
+ current_index.delete(id, options)
63
68
  end
64
69
 
65
70
  def count(query = nil, options = {})
@@ -97,12 +102,14 @@ module Workarea
97
102
  end
98
103
 
99
104
  def save(options = {})
100
- document = as_document.merge(Serializer.serialize(model))
101
- self.class.save(document, options)
105
+ I18n.for_each_locale do
106
+ document = as_document.merge(Serializer.serialize(model))
107
+ self.class.save(document, options)
108
+ end
102
109
  end
103
110
 
104
111
  def destroy(options = {})
105
- self.class.delete(id, options)
112
+ I18n.for_each_locale { self.class.delete(id, options) }
106
113
  end
107
114
  end
108
115
  end
@@ -0,0 +1,27 @@
1
+ module I18n
2
+ module JS
3
+ class FallbackLocales
4
+ # i18n-js uses just the second part of this check out-of-the-box. This
5
+ # causes the I18n fallbacks to get autoloaded without the developer
6
+ # knowing.
7
+ #
8
+ # This surfaces in tests. System or integration tests will do this check
9
+ # for compiling assets, then I18n fallbacks get autoloaded. So this shows
10
+ # as some tests not having fallbacks if they run before one of those tests
11
+ # or magically having fallbacks if they run after one of those types of
12
+ # tests.
13
+ #
14
+ # Adding the `respond_to?` check doesn't cause autoload, but will return
15
+ # `true` if fallbacks are enabled. Retain the original check because we
16
+ # want the current I18n::JS backend to be checked, once fallbacks are
17
+ # `require`d `I18n.respond_to?(:fallbacks)` will always return `true`.
18
+ #
19
+ # See also: https://github.com/fnando/i18n-js/blob/master/lib/i18n/js/fallback_locales.rb#L49-L58
20
+ #
21
+ def using_i18n_fallbacks_module?
22
+ I18n.respond_to?(:fallbacks) &&
23
+ I18n::JS.backend.class.included_modules.include?(I18n::Backend::Fallbacks)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ module Mongoid
2
+ module Fields
3
+ module LocalizedDefaults
4
+ def create_accessors(name, meth, options = {})
5
+ super
6
+
7
+ if options[:localize]
8
+ field = fields[name]
9
+
10
+ define_method meth do |*args|
11
+ result = super(*args)
12
+ return result unless result.nil?
13
+
14
+ default_name = field.send(:default_name)
15
+ return send(default_name) if respond_to?(default_name)
16
+
17
+ field.default_val
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ ClassMethods.prepend(LocalizedDefaults)
24
+ end
25
+ end
@@ -57,15 +57,7 @@ module Workarea
57
57
  end
58
58
 
59
59
  def names
60
- @names ||= [
61
- postal_code,
62
- city,
63
- region,
64
- subdivision&.name,
65
- country&.alpha2,
66
- country&.alpha3,
67
- country&.name
68
- ].reject(&:blank?)
60
+ @names ||= [postal_code, city, subdivision&.name, country&.name].reject(&:blank?)
69
61
  end
70
62
 
71
63
  private
@@ -2,7 +2,7 @@ module Workarea
2
2
  module VERSION
3
3
  MAJOR = 3
4
4
  MINOR = 5
5
- PATCH = 7
5
+ PATCH = 12
6
6
  PRE = nil
7
7
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
8
8
 
@@ -38,7 +38,10 @@ module Workarea
38
38
  end
39
39
 
40
40
  def current_email
41
- cookies.signed[:email]
41
+ # For performance, prefer to use the cookie. The fallback to looking it up
42
+ # by user is a failsafe against a blank email cookie (e.g. from a raised
43
+ # error or poor application coding).
44
+ cookies.signed[:email].presence || (email_from_user_id if logged_in?)
42
45
  end
43
46
 
44
47
  def metrics
@@ -91,5 +94,9 @@ module Workarea
91
94
  def blank_metrics
92
95
  @blank_metrics ||= Metrics::User.new
93
96
  end
97
+
98
+ def email_from_user_id
99
+ User.find(session[:user_id]).email rescue nil
100
+ end
94
101
  end
95
102
  end
@@ -51,13 +51,12 @@ db.getSiblingDB("admin").runCommand( { setParameter: 1, notablescan: 0 } )
51
51
  **************************************************
52
52
  ⛔️ WARNING: Dragonfly is configured to use the filesystem.
53
53
 
54
- This means all dragonfly assets (assets, product images, etc.) will be stored
54
+ This means all Dragonfly assets (assets, product images, etc.) will be stored
55
55
  locally and not accessible to all servers within your environment.
56
56
 
57
57
  We recommend using S3 when running in a live environment by setting
58
- WORKAREA_S3_REGION and WORKAREA_S3_BUCKET_NAME in your environment variables.
59
- Workarea will automatically configure Dragonfly to use S3 if those values
60
- are present.
58
+ WORKAREA_S3_REGION and WORKAREA_S3_BUCKET_NAME in your environment variables,
59
+ and setting `Workarea.config.asset_store = :s3` in an initializer.
61
60
  **************************************************
62
61
  eos
63
62
  end
@@ -7,12 +7,14 @@ module Workarea
7
7
  params[:controller] = 'controller'
8
8
  params[:action] = 'action'
9
9
  params[:foo] = 'bar'
10
+ params[:locale] = 'es'
10
11
 
11
12
  assert_includes(switch_locale_fields, 'foo')
12
13
  assert_includes(switch_locale_fields, 'bar')
13
14
  refute_includes(switch_locale_fields, 'utf8')
14
15
  refute_includes(switch_locale_fields, 'controller')
15
16
  refute_includes(switch_locale_fields, 'action')
17
+ refute_includes(switch_locale_fields, 'locale')
16
18
  end
17
19
  end
18
20
  end
@@ -185,5 +185,15 @@ module Workarea
185
185
  get '/test_logout'
186
186
  refute(cookies[:email].present?)
187
187
  end
188
+
189
+ def test_ensures_locale_passthrough_for_return_to
190
+ set_locales(available: [:en, :es], default: :en, current: :en)
191
+
192
+ get '/login_required', params: { locale: 'es', return_to: '/blah?foo=bar' }
193
+ get '/test_login', params: { user_id: @user.id }
194
+ assert(response.redirect?)
195
+ assert_match('locale=es', response.location)
196
+ assert_match('foo=bar', response.location)
197
+ end
188
198
  end
189
199
  end
@@ -25,6 +25,10 @@ module Workarea
25
25
  render plain: session[:foo].presence || 'nil'
26
26
  end
27
27
 
28
+ def varies
29
+ render plain: request.env['workarea.cache_varies']
30
+ end
31
+
28
32
  def current_user
29
33
  nil
30
34
  end
@@ -34,6 +38,11 @@ module Workarea
34
38
  Rails.application.routes.prepend do
35
39
  post 'cache_varies_test_set_session', to: 'workarea/cache_varies_integration_test/caching#set_session'
36
40
  get 'cache_varies_test_foo', to: 'workarea/cache_varies_integration_test/caching#foo'
41
+
42
+ scope '(:locale)', constraints: Workarea::I18n.routes_constraint do
43
+ get 'cache_varies_test_varies', to: 'workarea/cache_varies_integration_test/caching#varies'
44
+ patch 'cache_varies_test_varies', to: 'workarea/cache_varies_integration_test/caching#varies'
45
+ end
37
46
  end
38
47
 
39
48
  Rails.application.reload_routes!
@@ -74,5 +83,27 @@ module Workarea
74
83
  assert_equal('baz', response.body)
75
84
  assert_equal('fresh', response.headers['X-Rack-Cache'])
76
85
  end
86
+
87
+ def test_varies_includes_locale
88
+ set_locales(available: [:en, :es], default: :en, current: :en)
89
+
90
+ get '/cache_varies_test_varies'
91
+ assert_includes(response.body, 'en')
92
+
93
+ get '/cache_varies_test_varies', params: { locale: 'es' }
94
+ assert_includes(response.body, 'es')
95
+
96
+ get '/es/cache_varies_test_varies'
97
+ assert_includes(response.body, 'es')
98
+
99
+ patch '/cache_varies_test_varies'
100
+ assert_includes(response.body, 'en')
101
+
102
+ patch '/cache_varies_test_varies', params: { locale: 'es' }
103
+ assert_includes(response.body, 'es')
104
+
105
+ patch '/es/cache_varies_test_varies'
106
+ assert_includes(response.body, 'es')
107
+ end
77
108
  end
78
109
  end
@@ -3,27 +3,32 @@ require 'test_helper'
3
3
  module Workarea
4
4
  class MonitoringIntegrationTest < Workarea::IntegrationTest
5
5
  def test_monitors_the_mongodb_status
6
- get workarea.easymon_path('mongodb')
6
+ get workarea.easymon_path + '/mongodb'
7
7
  assert_includes(response.body, 'Up')
8
8
  end
9
9
 
10
10
  def test_monitors_the_redis_status
11
- get workarea.easymon_path('redis')
11
+ get workarea.easymon_path + '/redis'
12
12
  assert_includes(response.body, 'Up')
13
13
  end
14
14
 
15
15
  def test_monitors_the_elasticsearch_status
16
- get workarea.easymon_path('elasticsearch')
16
+ get workarea.easymon_path + '/elasticsearch'
17
17
  assert_includes(response.body, 'Up')
18
18
  end
19
19
 
20
20
  def test_monitors_the_sidekiq_queue_status
21
- get workarea.easymon_path('sidekiq-queue')
21
+ get workarea.easymon_path + '/sidekiq-queue'
22
22
  assert_includes(response.body, 'Low')
23
23
  end
24
24
 
25
25
  def test_monitors_for_load_balancing
26
- get workarea.easymon_path('load-balancing')
26
+ get workarea.easymon_path + '/load-balancing'
27
+ assert_includes(response.body, 'Up')
28
+ end
29
+
30
+ def test_critical_endpoint
31
+ get workarea.easymon_path + "/critical"
27
32
  assert_includes(response.body, 'Up')
28
33
  end
29
34
  end
@@ -67,6 +67,26 @@ module Workarea
67
67
  assert_equal({ 'id' => '1' }, results.first['_source'])
68
68
  end
69
69
 
70
+ def test_bulk_with_block
71
+ set_locales(available: [:en, :es], default: :en, current: :en)
72
+ Foo.bulk { { id: I18n.locale.to_s, bulk_action: 'index' } }
73
+
74
+ find_results = -> do
75
+ Foo
76
+ .current_index
77
+ .search({ query: { match_all: {} } }, type: 'foo')
78
+ .dig('hits', 'hits')
79
+ end
80
+
81
+ I18n.locale = :en
82
+ assert(1, Foo.count)
83
+ assert_equal({ 'id' => 'en' }, find_results.call.first['_source'])
84
+
85
+ I18n.locale = :es
86
+ assert(1, Foo.count)
87
+ assert_equal({ 'id' => 'es' }, find_results.call.first['_source'])
88
+ end
89
+
70
90
  def test_update
71
91
  Foo.save(id: '1')
72
92
  Foo.update(id: '1', foo: 'bar')
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ class MongoidLocalizedDefaultsTest < TestCase
5
+ class Foo
6
+ include Mongoid::Document
7
+
8
+ field :name, type: String, default: -> { 'foo' }, localize: true
9
+ field :config, type: Hash, default: { foo: 'bar' }, localize: true
10
+ end
11
+
12
+ def test_localized_defaults
13
+ set_locales(available: [:en, :es], default: :en, current: :en)
14
+
15
+ instance = Foo.new
16
+ assert_equal('foo', instance.name)
17
+ assert_equal({ foo: 'bar' }, instance.config)
18
+
19
+ I18n.locale = :es
20
+
21
+ assert_equal('foo', instance.name)
22
+ assert_equal({ foo: 'bar' }, instance.config)
23
+ end
24
+ end
25
+ end
@@ -94,10 +94,10 @@ module Workarea
94
94
  'HTTP_GEOIP_POSTAL_CODE' => '19106'
95
95
  )
96
96
 
97
- assert_includes(location.names, 'US')
98
- assert_includes(location.names, 'USA')
97
+ refute_includes(location.names, 'US')
98
+ refute_includes(location.names, 'USA')
99
99
  assert_includes(location.names, 'United States of America')
100
- assert_includes(location.names, 'PA')
100
+ refute_includes(location.names, 'PA')
101
101
  assert_includes(location.names, 'Pennsylvania')
102
102
  assert_includes(location.names, 'Philadelphia')
103
103
  assert_includes(location.names, '19106')
@@ -10,6 +10,21 @@ module Workarea
10
10
  model.update!(active: true)
11
11
  assert(Storefront.new(model).active[:now])
12
12
  end
13
+
14
+ def test_changesets
15
+ category = create_category(
16
+ name: 'Foo',
17
+ product_rules: [{ name: 'search', operator: 'equals', value: 'foo' }]
18
+ )
19
+ assert_empty(Storefront.new(category).changesets)
20
+
21
+ release = create_release
22
+ release.as_current { category.update!(name: 'Bar') }
23
+ assert_equal(1, Storefront.new(category).changesets.size)
24
+
25
+ release.as_current { category.product_rules.first.update!(value: 'bar') }
26
+ assert_equal(2, Storefront.new(category).changesets.size)
27
+ end
13
28
  end
14
29
  end
15
30
  end
@@ -7,12 +7,15 @@ module Workarea
7
7
  def test_qualifies?
8
8
  refute(Geolocation.new.qualifies?(create_visit))
9
9
 
10
- visit = create_visit('HTTP_GEOIP_REGION' => 'PA')
10
+ visit = create_visit(
11
+ 'HTTP_GEOIP_REGION' => 'PA',
12
+ 'HTTP_GEOIP_CITY_COUNTRY_CODE' => 'US'
13
+ )
11
14
  refute(Geolocation.new(locations: %w(NJ)).qualifies?(visit))
12
- assert(Geolocation.new(locations: %w(pa)).qualifies?(visit))
13
- assert(Geolocation.new(locations: %w(PA)).qualifies?(visit))
14
- assert(Geolocation.new(locations: %w(NJ PA)).qualifies?(visit))
15
- refute(Geolocation.new(locations: %w(NJ NY)).qualifies?(visit))
15
+ assert(Geolocation.new(locations: %w(us-pa)).qualifies?(visit))
16
+ assert(Geolocation.new(locations: %w(US-PA)).qualifies?(visit))
17
+ assert(Geolocation.new(locations: %w(US-NJ US-pa)).qualifies?(visit))
18
+ refute(Geolocation.new(locations: %w(US-NJ US-NY)).qualifies?(visit))
16
19
 
17
20
  visit = create_visit(
18
21
  'HTTP_GEOIP_REGION' => 'PA',
@@ -26,14 +29,13 @@ module Workarea
26
29
  assert(Geolocation.new(locations: %w(Philadelphia)).qualifies?(visit))
27
30
  assert(Geolocation.new(locations: %w(Philadelphia US)).qualifies?(visit))
28
31
  refute(Geolocation.new(locations: %w(Harrisburg)).qualifies?(visit))
29
- refute(Geolocation.new(locations: %w(Harrisburg PA)).qualifies?(visit))
32
+ refute(Geolocation.new(locations: %w(Harrisburg US-PA)).qualifies?(visit))
30
33
  assert(Geolocation.new(locations: %w(Philadelphia Harrisburg)).qualifies?(visit))
31
34
  refute(Geolocation.new(locations: %w(Pittsburgh Harrisburg)).qualifies?(visit))
32
35
 
33
36
  visit = create_visit('HTTP_GEOIP_CITY_COUNTRY_CODE' => 'US')
34
37
  refute(Geolocation.new(locations: %w(CA)).qualifies?(visit))
35
38
  assert(Geolocation.new(locations: %w(US)).qualifies?(visit))
36
- assert(Geolocation.new(locations: %w(USA)).qualifies?(visit))
37
39
  assert(Geolocation.new(locations: ['United States of America']).qualifies?(visit))
38
40
  assert(Geolocation.new(locations: %w(Philadelphia US)).qualifies?(visit))
39
41
  assert(Geolocation.new(locations: %w(US CA)).qualifies?(visit))
@@ -4,14 +4,22 @@ module Workarea
4
4
  class User
5
5
  class PasswordResetTest < TestCase
6
6
  def user
7
- @user ||= create_user
7
+ @user ||= create_user(email: 'one@workarea.com')
8
8
  end
9
9
 
10
10
  def test_setup!
11
- PasswordReset.setup!(user.email)
11
+ 2.times do
12
+ PasswordReset.setup!(user.email)
12
13
 
13
- assert_equal(1, PasswordReset.count)
14
- assert_equal(user.id, PasswordReset.first.user_id)
14
+ assert_equal(1, PasswordReset.count)
15
+ assert_equal(user.id, PasswordReset.first.user_id)
16
+ end
17
+
18
+ two = create_user(email: 'two@workarea.com')
19
+ PasswordReset.setup!('two@workarea.com')
20
+
21
+ assert_equal(2, PasswordReset.count)
22
+ assert_equal(two.id, PasswordReset.last.user_id)
15
23
  end
16
24
 
17
25
  def test_complete
@@ -19,6 +19,15 @@ module Workarea
19
19
  assert_equal(1, Paginate.new(page: 'asdf').page)
20
20
  end
21
21
 
22
+ def test_per_page
23
+ assert_equal(Workarea.config.per_page, Paginate.new.per_page)
24
+ assert_equal(2, Paginate.new(per_page: 2).per_page)
25
+ assert_equal(Workarea.config.per_page, Paginate.new(per_page: -1).per_page)
26
+ assert_equal(Workarea.config.per_page, Paginate.new(per_page: 0).per_page)
27
+ assert_equal(3, Paginate.new(per_page: '3').per_page)
28
+ assert_equal(Workarea.config.per_page, Paginate.new(per_page: 'asdf').per_page)
29
+ end
30
+
22
31
  def test_from
23
32
  Workarea.config.per_page = 30
24
33
  assert_equal(0, Paginate.new(page: 1).from)
@@ -36,6 +36,17 @@ module Workarea
36
36
  assert_equal(release.id, results.first.release_id)
37
37
  refute_equal(product.object_id, results.first.model.object_id)
38
38
  end
39
+
40
+ def test_entry_flattening
41
+ products = Array.new(2) { create_product }
42
+ release = create_release
43
+ release.as_current { products.first.update!(name: 'Bar') }
44
+
45
+ instance = ProductEntries.new(products)
46
+ instance.stubs(:index_entries_for).returns([:foo, :bar])
47
+
48
+ assert_equal([:foo, :bar, :foo, :bar, :foo, :bar], instance.entries)
49
+ end
39
50
  end
40
51
  end
41
52
  end
@@ -410,6 +410,37 @@ module Workarea
410
410
  assert(result[:raw].present?)
411
411
  assert_kind_of(Float, result[:raw]['_score'])
412
412
  end
413
+
414
+ def test_previewing_releases
415
+ product = create_product(id: 'foo', variants: [{ sku: '1234', regular: 5 }])
416
+ pricing = Pricing::Sku.find('1234')
417
+ assert_equal([product], ProductSearch.new(q: '*').results.pluck(:model))
418
+
419
+ release = create_release
420
+ release.as_current { pricing.prices.first.update!(regular: 10) }
421
+ IndexProduct.perform(product.reload)
422
+
423
+ assert_equal([product], ProductSearch.new(q: '*').results.pluck(:model))
424
+ release.as_current do
425
+ assert_equal([product], ProductSearch.new(q: '*').results.pluck(:model))
426
+ end
427
+ end
428
+
429
+ def test_locale
430
+ # No simple way to run this test without fallbacks or localized fields
431
+ return unless Workarea.config.localized_active_fields
432
+
433
+ set_locales(available: [:en, :es], default: :en, current: :en)
434
+ Search::Storefront.reset_indexes!
435
+
436
+ product = create_product(active_translations: { 'en' => true, 'es' => false })
437
+
438
+ I18n.locale = :es
439
+ assert_equal([], ProductSearch.new(q: '*').results.pluck(:model))
440
+
441
+ I18n.locale = :en
442
+ assert_equal([product], ProductSearch.new(q: '*').results.pluck(:model))
443
+ end
413
444
  end
414
445
  end
415
446
  end
@@ -108,11 +108,8 @@ module Workarea
108
108
  ).returns(true)
109
109
 
110
110
  assert(DirectUpload.ensure_cors!('http://test.host/admin/content_assets'))
111
- assert_equal('true', Workarea.redis.get('cors_http_test_host'))
112
111
  assert(DirectUpload.ensure_cors!('http://localhost:3000/admin/content_assets'))
113
- assert_equal('true', Workarea.redis.get('cors_http_localhost_3000'))
114
112
  assert(DirectUpload.ensure_cors!('https://example.com/admin/direct_uploads'))
115
- assert_equal('true', Workarea.redis.get('cors_https_example_com'))
116
113
  end
117
114
 
118
115
  private
@@ -2,23 +2,23 @@ require 'test_helper'
2
2
 
3
3
  module Workarea
4
4
  class HashUpdateTest < TestCase
5
- def test_apply
6
- hash = { 'foo' => 'bar' }
5
+ def test_result
6
+ original = { 'foo' => 'bar' }
7
7
 
8
- HashUpdate.new(adds: %w(key value)).apply(hash)
9
- assert_equal(%w(value), hash['key'])
8
+ result = HashUpdate.new(original: original, adds: %w(key value)).result
9
+ assert_equal(%w(value), result['key'])
10
10
 
11
- HashUpdate.new(updates: %w(foo baz)).apply(hash)
12
- assert_equal(%w(baz), hash['foo'])
11
+ result = HashUpdate.new(original: original, updates: %w(foo baz)).result
12
+ assert_equal(%w(baz), result['foo'])
13
13
 
14
- HashUpdate.new(removes: %w(foo)).apply(hash)
15
- refute_includes(hash.keys, 'foo')
14
+ result = HashUpdate.new(original: original, removes: %w(foo)).result
15
+ refute_includes(result.keys, 'foo')
16
16
 
17
- HashUpdate.new(adds: ['key', 'one, two ']).apply(hash)
18
- assert_equal(%w(one two), hash['key'])
17
+ result = HashUpdate.new(original: original, adds: ['key', 'one, two ']).result
18
+ assert_equal(%w(one two), result['key'])
19
19
 
20
- HashUpdate.new(updates: ['key', 'one, two, three ']).apply(hash)
21
- assert_equal(%w(one two three), hash['key'])
20
+ result = HashUpdate.new(original: original, updates: ['key', 'one, two, three ']).result
21
+ assert_equal(%w(one two three), result['key'])
22
22
  end
23
23
  end
24
24
  end
@@ -53,5 +53,11 @@ module Workarea
53
53
  t('workarea.admin.data_file_mailer.import_failure.errors')
54
54
  )
55
55
  end
56
+
57
+ def test_perform_with_missing_import
58
+ assert_raises Mongoid::Errors::DocumentNotFound do
59
+ ProcessImport.new.perform('foo')
60
+ end
61
+ end
56
62
  end
57
63
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: workarea-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.7
4
+ version: 3.5.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Crouse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-17 00:00:00.000000000 Z
11
+ date: 2020-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -1919,7 +1919,9 @@ files:
1919
1919
  - lib/workarea/ext/freedom_patches/dragonfly_job_fetch_url.rb
1920
1920
  - lib/workarea/ext/freedom_patches/float.rb
1921
1921
  - lib/workarea/ext/freedom_patches/global_id.rb
1922
+ - lib/workarea/ext/freedom_patches/i18n_js.rb
1922
1923
  - lib/workarea/ext/freedom_patches/money.rb
1924
+ - lib/workarea/ext/freedom_patches/mongoid_localized_defaults.rb
1923
1925
  - lib/workarea/ext/freedom_patches/mongoid_simple_tags.rb
1924
1926
  - lib/workarea/ext/freedom_patches/net_http_ssl_connection.rb
1925
1927
  - lib/workarea/ext/freedom_patches/premailer.rb
@@ -2081,6 +2083,7 @@ files:
2081
2083
  - test/lib/workarea/ext/freedom_patches/dragonfly_callable_url_host_test.rb
2082
2084
  - test/lib/workarea/ext/freedom_patches/global_id_test.rb
2083
2085
  - test/lib/workarea/ext/freedom_patches/money_test.rb
2086
+ - test/lib/workarea/ext/freedom_patches/mongoid_localized_defaults_test.rb
2084
2087
  - test/lib/workarea/ext/freedom_patches/mongoid_simple_tags_test.rb
2085
2088
  - test/lib/workarea/ext/mongoid/audit_log_entry_test.rb
2086
2089
  - test/lib/workarea/ext/mongoid/each_by_test.rb
@@ -2718,7 +2721,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
2718
2721
  - !ruby/object:Gem::Version
2719
2722
  version: '0'
2720
2723
  requirements: []
2721
- rubygems_version: 3.0.6
2724
+ rubygems_version: 3.0.3
2722
2725
  signing_key:
2723
2726
  specification_version: 4
2724
2727
  summary: Core of the Workarea Commerce Platform