workarea-core 3.5.15 → 3.5.20

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/workarea/current_tracking.rb +4 -2
  3. data/app/controllers/workarea/impersonation.rb +4 -2
  4. data/app/mailers/workarea/application_mailer.rb +4 -1
  5. data/app/middleware/workarea/application_middleware.rb +5 -2
  6. data/app/models/workarea/checkout.rb +7 -7
  7. data/app/models/workarea/data_file/csv.rb +9 -1
  8. data/app/models/workarea/inquiry.rb +2 -1
  9. data/app/models/workarea/inventory/sku.rb +2 -2
  10. data/app/models/workarea/metrics/user.rb +24 -8
  11. data/app/models/workarea/order.rb +10 -0
  12. data/app/models/workarea/order/item.rb +4 -4
  13. data/app/models/workarea/payment.rb +1 -6
  14. data/app/models/workarea/search/admin/inventory_sku.rb +5 -1
  15. data/app/models/workarea/shipping/service.rb +12 -5
  16. data/app/models/workarea/tax/rate.rb +3 -1
  17. data/app/queries/workarea/search/admin_search.rb +4 -0
  18. data/app/queries/workarea/search/admin_sorting.rb +1 -1
  19. data/config/initializers/00_configuration.rb +23 -8
  20. data/config/initializers/22_session_store.rb +1 -1
  21. data/config/locales/en.yml +3 -0
  22. data/lib/generators/workarea/install/install_generator.rb +13 -0
  23. data/lib/generators/workarea/install/templates/initializer.rb.erb +1 -13
  24. data/lib/tasks/cache.rake +3 -33
  25. data/lib/tasks/help.rake +4 -43
  26. data/lib/tasks/insights.rake +3 -35
  27. data/lib/tasks/migrate.rake +3 -96
  28. data/lib/tasks/search.rake +6 -68
  29. data/lib/tasks/services.rake +4 -54
  30. data/lib/workarea/configuration.rb +11 -2
  31. data/lib/workarea/configuration/administrable_options.rb +1 -5
  32. data/lib/workarea/core/engine.rb +4 -0
  33. data/lib/workarea/tasks/cache.rb +43 -0
  34. data/lib/workarea/tasks/help.rb +55 -0
  35. data/lib/workarea/tasks/insights.rb +47 -0
  36. data/lib/workarea/tasks/migrate.rb +106 -0
  37. data/lib/workarea/tasks/search.rb +105 -0
  38. data/lib/workarea/tasks/services.rb +71 -0
  39. data/lib/workarea/version.rb +1 -1
  40. data/lib/workarea/visit.rb +8 -1
  41. data/test/generators/workarea/install_generator_test.rb +6 -2
  42. data/test/integration/workarea/authentication_test.rb +2 -1
  43. data/test/mailers/workarea/application_mailer_test.rb +10 -0
  44. data/test/models/workarea/checkout_test.rb +57 -0
  45. data/test/models/workarea/data_file/import_test.rb +40 -0
  46. data/test/models/workarea/order/item_test.rb +9 -0
  47. data/test/models/workarea/shipping/service_test.rb +26 -0
  48. data/test/queries/workarea/search/admin_search_test.rb +10 -0
  49. data/workarea-core.gemspec +1 -1
  50. metadata +10 -4
@@ -26,8 +26,6 @@ module Workarea
26
26
 
27
27
  config.site_name = 'Workarea'
28
28
  config.host = 'www.example.com'
29
- config.email_to = 'customerservice@example.com'
30
- config.email_from = 'noreply@example.com'
31
29
 
32
30
  # Config sent to the ImageMagick through Dragonfly for optimizing jpgs
33
31
  # All metadata profiles are removed, comments cleared by comment -set ""
@@ -1297,6 +1295,17 @@ module Workarea
1297
1295
 
1298
1296
  # The number of results that will show per-type in the admin jump to
1299
1297
  config.jump_to_results_per_type = 5
1298
+
1299
+ # Attributes that will be sliced out of params and persisted on
1300
+ # the credit card tender during checkout.
1301
+ config.credit_card_attributes = %i[
1302
+ month
1303
+ year
1304
+ saved_card_id
1305
+ number
1306
+ cvv
1307
+ amount
1308
+ ]
1300
1309
  end
1301
1310
  end
1302
1311
  end
@@ -1,7 +1,7 @@
1
1
  module Workarea
2
2
  module Configuration
3
3
  class AdministrableOptions < ActiveSupport::InheritableOptions
4
- def method_missing(name, *args)
4
+ def [](name)
5
5
  static_config = super
6
6
  return static_config if static_config.present? || static_config.to_s == 'false'
7
7
  return static_config unless check_fieldsets?(name)
@@ -9,10 +9,6 @@ module Workarea
9
9
  Configuration::Admin.instance.send(name)
10
10
  end
11
11
 
12
- def respond_to_missing?(name, include_private)
13
- true
14
- end
15
-
16
12
  private
17
13
 
18
14
  def check_fieldsets?(name)
@@ -78,6 +78,10 @@ module Workarea
78
78
  # get autoloaded. Without this, admin actions like updating product
79
79
  # attributes raises a {NameError} "uninitialized constant BulkIndexProducts".
80
80
  require_dependency 'workarea/bulk_index_products'
81
+
82
+ # Fixes a constant error raised in middleware (when doing segmentation)
83
+ # No idea what the cause is. TODO revisit after Zeitwerk.
84
+ require_dependency 'workarea/metrics/user'
81
85
  end
82
86
  end
83
87
  end
@@ -0,0 +1,43 @@
1
+ module Workarea
2
+ module Tasks
3
+ module Cache
4
+ extend self
5
+
6
+ def prime_images
7
+ include Rails.application.routes.url_helpers
8
+ include Workarea::Storefront::ProductsHelper
9
+ include Workarea::Core::Engine.routes.url_helpers
10
+
11
+ built_in_jobs = [:thumb, :gif, :jpg, :png, :strip, :convert, :optimized]
12
+
13
+ jobs = Dragonfly.app(:workarea).processor_methods.reject do |job|
14
+ built_in_jobs.include?(job)
15
+ end
16
+
17
+ Workarea::Catalog::Product.all.each_by(50) do |product|
18
+ product.images.each do |image|
19
+ jobs.each do |job|
20
+ url = URI.join(
21
+ "https://#{Workarea.config.host}",
22
+ dynamic_product_image_url(
23
+ image.product.slug,
24
+ image.option,
25
+ image.id,
26
+ job,
27
+ only_path: true
28
+ )
29
+ ).to_s
30
+
31
+ begin
32
+ `curl #{url}`
33
+ puts "Downloaded image #{url}"
34
+ rescue StandardError => e
35
+ puts e.inspect
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,55 @@
1
+ module Workarea
2
+ module Tasks
3
+ module Help
4
+ extend self
5
+
6
+ def reload
7
+ Workarea::Help::Article.delete_all
8
+ Workarea::Help::Asset.delete_all
9
+ Workarea::HelpSeeds.new.perform
10
+ end
11
+
12
+ def dump
13
+ Workarea::Help::Article.all.each_by(50) do |article|
14
+ article_root = Rails.root.join(
15
+ 'data',
16
+ 'help',
17
+ article.category.systemize,
18
+ article.name.systemize
19
+ )
20
+
21
+ asset_path = article_root.join('assets')
22
+
23
+ FileUtils.mkdir_p(article_root)
24
+
25
+ if article.thumbnail.present?
26
+ article.thumbnail.to_file(article_root.join(article.thumbnail.name))
27
+ end
28
+
29
+ Workarea::Help::Asset.all.each_by(50) do |asset|
30
+ if article.summary.include?(asset.url) || article.body.include?(asset.url)
31
+ FileUtils.mkdir_p(asset_path)
32
+ asset.to_file(asset_path.join(asset.name))
33
+ reference = "<%= #{asset.name.split('.').first} %>"
34
+
35
+ article.summary.gsub!(asset.url, reference)
36
+ article.body.gsub!(asset.url, reference)
37
+ end
38
+ end
39
+
40
+ if article.summary.present?
41
+ File.open(article_root.join('summary.md'), 'w') do |file|
42
+ file.write(article.summary)
43
+ end
44
+ end
45
+
46
+ if article.body.present?
47
+ File.open(article_root.join('body.md'), 'w') do |file|
48
+ file.write(article.body)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ module Workarea
2
+ module Tasks
3
+ module Insights
4
+ extend self
5
+
6
+ def generate
7
+ require 'active_support/testing/time_helpers'
8
+ include ActiveSupport::Testing::TimeHelpers
9
+ batch_size = ENV['WORKAREA_INSIGHTS_BATCH_SIZE'].presence || 1000
10
+
11
+ Workarea::Order
12
+ .placed
13
+ .each_by(batch_size.to_i) { |o| Workarea::SaveOrderMetrics.perform(o) }
14
+
15
+ 8.times do |i|
16
+ travel_to (i.weeks.ago.beginning_of_week + 1.hour)
17
+ Workarea::GenerateInsights.generate_all!
18
+ end
19
+ end
20
+
21
+ # Clear the metrics/insights environment - deletes lots of data, this task
22
+ # is very dangerous! Useful for testing/debugging.
23
+ def reset!
24
+ Workarea::Order
25
+ .where(:metrics_saved_at.gt => 0)
26
+ .update_all(metrics_saved_at: nil)
27
+
28
+ Workarea::Metrics::CategoryByDay.delete_all
29
+ Workarea::Metrics::CountryByDay.delete_all
30
+ Workarea::Metrics::DiscountByDay.delete_all
31
+ Workarea::Metrics::MenuByDay.delete_all
32
+ Workarea::Metrics::ProductByDay.delete_all
33
+ Workarea::Metrics::ProductByWeek.delete_all
34
+ Workarea::Metrics::ProductForLastWeek.delete_all
35
+ Workarea::Metrics::SalesByDay.delete_all
36
+ Workarea::Metrics::SearchByDay.delete_all
37
+ Workarea::Metrics::SearchByWeek.delete_all
38
+ Workarea::Metrics::SearchForLastWeek.delete_all
39
+ Workarea::Metrics::SkuByDay.delete_all
40
+ Workarea::Metrics::TenderByDay.delete_all
41
+ Workarea::Metrics::TrafficReferrerByDay.delete_all
42
+ Workarea::Metrics::User.delete_all
43
+ Workarea::Insights::Base.delete_all
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,106 @@
1
+ module Workarea
2
+ module Tasks
3
+ module Migrate
4
+ extend self
5
+
6
+ def v3_5
7
+ count = 0
8
+
9
+ Workarea::Release.where(:undo_at.gte => Time.current).each do |release|
10
+ undo = release.build_undo(publish_at: release.undo_at).tap(&:save!)
11
+
12
+ release.changesets.each do |changeset|
13
+ changeset.build_undo(release: undo).save!
14
+ end
15
+
16
+ Workarea::Scheduler.delete(release.undo_job_id)
17
+
18
+ release.update_attributes!(undo_at: nil, undo_job_id: nil)
19
+ count += 1
20
+ end
21
+
22
+ Workarea::Release.all.each { |r| Workarea::IndexAdminSearch.perform(r) }
23
+
24
+ puts "✅ #{count} undo releases have been created."
25
+
26
+ count = 0
27
+
28
+ Workarea::Tax::Category.all.each_by(100) do |category|
29
+ category.rates.each_by(500) do |rate|
30
+ rate.postal_code_percentage = rate.percentage
31
+ rate.percentage = nil
32
+ end
33
+
34
+ category.save!
35
+ count += 1
36
+ end
37
+
38
+ puts "✅ #{count} tax categories updated."
39
+
40
+ count = 0
41
+ failed_ids = []
42
+ backup = Mongo::Collection.new(Mongoid::Clients.default.database, 'workarea_legacy_segments')
43
+
44
+ legacy_segments = Workarea::Segment.collection.find.to_a
45
+ legacy_segments.each do |doc|
46
+ backup.insert_one(doc)
47
+ Workarea::Segment.collection.delete_one(doc.slice('_id'))
48
+
49
+ segment = Workarea::Segment.new(
50
+ id: doc['_id'],
51
+ name: doc['name'],
52
+ subscribed_user_ids: doc['subscribed_user_ids'],
53
+ created_at: doc['created_at'],
54
+ updated_at: doc['updated_at']
55
+ )
56
+
57
+ doc['conditions'].each do |condition|
58
+ if condition['_type'] =~ /UserTag/
59
+ segment.rules << Workarea::Segment::Rules::Tags.new(tags: condition['tags'])
60
+ elsif condition['_type'] =~ /TotalSpent/
61
+ rule = Workarea::Segment::Rules::Revenue.new
62
+
63
+ if condition['operator'] == 'equals'
64
+ rule.minimum = rule.maximum = Money.demongoize(condition['amount'])
65
+ elsif condition['operator'] == 'less_than_or_equals'
66
+ rule.maximum = Money.demongoize(condition['amount'])
67
+ elsif condition['operator'] == 'less_than'
68
+ rule.maximum = (Money.demongoize(condition['amount']) - 0.01.to_m)
69
+ elsif condition['operator'] == 'greater_than_or_equals'
70
+ rule.minimum = Money.demongoize(condition['amount'])
71
+ elsif condition['operator'] == 'greater_than'
72
+ rule.minimum = (Money.demongoize(condition['amount']) + 0.01.to_m)
73
+ end
74
+
75
+ segment.rules << rule
76
+ end
77
+ end
78
+
79
+ if doc['conditions'].size == segment.rules.size && segment.save
80
+ count += 1
81
+ else
82
+ failed_ids << doc['_id']
83
+ end
84
+ end
85
+
86
+ puts "✅ #{count} segments have been migrated." if count > 0
87
+ if failed_ids.any?
88
+ puts "⛔️ #{failed_ids.count} segments failed to migrate."
89
+ puts "You can find copies of the original segments in the workarea_legacy_segments collection."
90
+ puts "The segments that failed are #{failed_ids.to_sentence}."
91
+ end
92
+
93
+ Workarea::Segment::LifeCycle.create!
94
+ puts "✅ Life cycle segments have been created."
95
+
96
+ admin_ids = Workarea::User.admins.pluck(:id)
97
+ admin_ids.each do |id|
98
+ Workarea::SynchronizeUserMetrics.new.perform(id)
99
+ end
100
+ puts "✅ #{admin_ids.count} admins have had their metrics synchronized." if admin_ids.count > 0
101
+
102
+ puts "\nMigration complete!"
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,105 @@
1
+ module Workarea
2
+ module Tasks
3
+ module Search
4
+ extend self
5
+
6
+ def setup
7
+ require 'sidekiq/testing/inline' unless ENV['INLINE'] == 'false'
8
+ Workarea.config.bulk_index_batch_size = ENV['BATCH_SIZE'].to_i if ENV['BATCH_SIZE'].present?
9
+ end
10
+
11
+ def index_admin
12
+ Workarea::QueuesPauser.with_paused_queues do
13
+ Workarea::Search::Admin.reset_indexes!
14
+ end
15
+
16
+ Mongoid.models.each do |klass|
17
+ next unless Workarea::Search::Admin.for(klass.first).present?
18
+
19
+ klass.all.each_slice_of(Workarea.config.bulk_index_batch_size) do |models|
20
+ Workarea::BulkIndexAdmin.perform_by_models(models)
21
+ end
22
+ end
23
+
24
+ Workarea.config.jump_to_navigation.to_a.each do |tuple|
25
+ Workarea::Search::Admin::Navigation.new(tuple).save
26
+ end
27
+ end
28
+
29
+ def index_storefront
30
+ Workarea::QueuesPauser.with_paused_queues do
31
+ Workarea::Search::Storefront.reset_indexes!
32
+ Workarea::Search::Storefront.ensure_dynamic_mappings
33
+ end
34
+
35
+ ensure_dynamic_mappings_for_current_product_filters
36
+
37
+ index_storefront_categories
38
+ index_storefront_content_pages
39
+ index_storefront_products
40
+ index_storefront_searches
41
+ end
42
+
43
+ def index_help
44
+ Workarea::QueuesPauser.with_paused_queues do
45
+ Workarea::Search::Help.reset_indexes!
46
+ end
47
+
48
+ Workarea::Help::Article.all.each_by(Workarea.config.bulk_index_batch_size) do |help_article|
49
+ Workarea::Search::Help.new(help_article).save
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # This code finds all unique filters for products so we can index a sample
56
+ # product for each to ensure the dynamic mappings get created.
57
+ #
58
+ # This is necessary to fix mapping errors from Elasticsearch when trying
59
+ # to index category percolations against fields which have no mapping.
60
+ #
61
+ def ensure_dynamic_mappings_for_current_product_filters
62
+ map = %{
63
+ function() {
64
+ for (var key in this.filters.#{I18n.locale}) {
65
+ emit(key, null);
66
+ }
67
+ }
68
+ }
69
+ reduce = 'function(key) { return null; }'
70
+ results = Workarea::Catalog::Product.map_reduce(map, reduce).out(inline: 1)
71
+ unique_filters = results.map { |r| r['_id'] }
72
+
73
+ sample_products = unique_filters.reduce([]) do |memo, filter|
74
+ filter = "filters.#{I18n.locale}.#{filter}"
75
+ memo << Workarea::Catalog::Product.exists(filter => true).sample
76
+ end
77
+
78
+ sample_products.each do |product|
79
+ Workarea::Search::Storefront::Product.new(product).save
80
+ end
81
+ end
82
+
83
+ def index_storefront_categories
84
+ Workarea::Catalog::Category.all.each_by(Workarea.config.bulk_index_batch_size) do |category|
85
+ Workarea::Search::Storefront::CategoryQuery.new(category).create
86
+ Workarea::Search::Storefront::Category.new(category).save
87
+ end
88
+ end
89
+
90
+ def index_storefront_content_pages
91
+ Workarea::Content::Page.all.each_by(Workarea.config.bulk_index_batch_size) do |page|
92
+ Workarea::Search::Storefront::Page.new(page).save
93
+ end
94
+ end
95
+
96
+ def index_storefront_products
97
+ Workarea::BulkIndexProducts.perform
98
+ end
99
+
100
+ def index_storefront_searches
101
+ Workarea::BulkIndexSearches.perform
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,71 @@
1
+ module Workarea
2
+ module Tasks
3
+ module Services
4
+ extend self
5
+
6
+ def assert_docker_compose_installed!
7
+ unless system('docker-compose -v > /dev/null 2>&1')
8
+ STDERR.puts <<~eos
9
+ **************************************************
10
+ ⛔️ ERROR: workarea:services tasks depend on Docker Compose being installed. \
11
+ See https://docs.docker.com/compose/install/ for how to install.
12
+ **************************************************
13
+ eos
14
+ exit
15
+ end
16
+ end
17
+
18
+ def compose_file_path
19
+ File.join(Gem::Specification.find_by_name('workarea').gem_dir, 'docker-compose.yml')
20
+ end
21
+
22
+ def compose_env
23
+ require 'workarea/version'
24
+
25
+ {
26
+ 'COMPOSE_FILE' => compose_file_path,
27
+ 'COMPOSE_PROJECT_NAME' => File.basename(Dir.pwd),
28
+
29
+ 'MONGODB_VERSION' => Workarea::VERSION::MONGODB::STRING,
30
+ 'MONGODB_PORT' => ENV['WORKAREA_MONGODB_PORT'] || '27017',
31
+
32
+ 'REDIS_VERSION' => Workarea::VERSION::REDIS::STRING,
33
+ 'REDIS_PORT' => ENV['WORKAREA_REDIS_PORT'] || '6379',
34
+
35
+ 'ELASTICSEARCH_VERSION' => Workarea::VERSION::ELASTICSEARCH::STRING,
36
+ 'ELASTICSEARCH_PORT' => ENV['WORKAREA_ELASTICSEARCH_PORT'] || '9200'
37
+ }
38
+ end
39
+
40
+ def up
41
+ assert_docker_compose_installed!
42
+
43
+ if system(compose_env, "docker-compose up -d #{ENV['COMPOSE_ARGUMENTS']} #{ENV['WORKAREA_SERVICES']}")
44
+ puts '✅ Success! Workarea services are running in the background. Run workarea:services:down to stop them.'
45
+ else
46
+ STDERR.puts '⛔️ Error! There was an error starting Workarea services.'
47
+ end
48
+ end
49
+
50
+ def down
51
+ assert_docker_compose_installed!
52
+
53
+ if system(compose_env, "docker-compose down #{ENV['COMPOSE_ARGUMENTS']}")
54
+ puts '✅ Success! Workarea services are stopped. Run workarea:services:up to start them.'
55
+ else
56
+ STDERR.puts '⛔️ Error! There was an error stopping Workarea services.'
57
+ end
58
+ end
59
+
60
+ def clean
61
+ assert_docker_compose_installed!
62
+
63
+ if system(compose_env, "docker-compose down -v #{ENV['COMPOSE_ARGUMENTS']}")
64
+ puts '✅ Success! Workarea service volumes have been removed. Run workarea:services:up to start services and recreate volumes.'
65
+ else
66
+ STDERR.puts '⛔️ Error! There was an error removing Workarea service volumes.'
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end