From 5c77eed40f1a37d2483e748f7fa98c81778bdb5a Mon Sep 17 00:00:00 2001 From: Koen Gremmelprez Date: Fri, 18 Oct 2024 12:57:38 +0200 Subject: [PATCH 1/3] TAN-2820 Aggressive caching engine and setup --- back/Gemfile | 1 + back/Gemfile.lock | 10 ++++++ .../app/controllers/application_controller.rb | 2 ++ .../v1/app_configurations_controller.rb | 2 ++ .../aggressive_caching.gemspec | 22 ++++++++++++ .../patches/application_controller.rb | 21 ++++++++++++ .../v1/app_configurations_controller.rb | 21 ++++++++++++ .../lib/aggressive_caching.rb | 8 +++++ .../lib/aggressive_caching/engine.rb | 12 +++++++ .../feature_specification.rb | 34 +++++++++++++++++++ .../lib/aggressive_caching/version.rb | 5 +++ 11 files changed, 138 insertions(+) create mode 100644 back/engines/commercial/aggressive_caching/aggressive_caching.gemspec create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/lib/aggressive_caching.rb create mode 100644 back/engines/commercial/aggressive_caching/lib/aggressive_caching/engine.rb create mode 100644 back/engines/commercial/aggressive_caching/lib/aggressive_caching/feature_specification.rb create mode 100644 back/engines/commercial/aggressive_caching/lib/aggressive_caching/version.rb diff --git a/back/Gemfile b/back/Gemfile index 0293df1d6ccb..ab45046154a9 100644 --- a/back/Gemfile +++ b/back/Gemfile @@ -199,6 +199,7 @@ commercial_engines = [ 'multi_tenancy', 'admin_api', + 'aggressive_caching', 'analysis', 'analytics', 'bulk_import_ideas', diff --git a/back/Gemfile.lock b/back/Gemfile.lock index f17505f6845c..54c6ed258cb9 100644 --- a/back/Gemfile.lock +++ b/back/Gemfile.lock @@ -9,6 +9,13 @@ PATH rails (~> 7.0) ros-apartment (>= 2.9.0) +PATH + remote: engines/commercial/aggressive_caching + specs: + aggressive_caching (0.1.0) + actionpack-action_caching (~> 1.2) + rails (~> 7.0) + PATH remote: engines/commercial/analysis specs: @@ -402,6 +409,8 @@ GEM rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) + actionpack-action_caching (1.2.2) + actionpack (>= 4.0.0) actiontext (7.0.8.5) actionpack (= 7.0.8.5) activerecord (= 7.0.8.5) @@ -1245,6 +1254,7 @@ DEPENDENCIES activerecord-postgis-adapter (~> 8.0) acts_as_list (~> 1.1) admin_api! + aggressive_caching! analysis! analytics! annotate diff --git a/back/app/controllers/application_controller.rb b/back/app/controllers/application_controller.rb index 3994e257121e..88d586da6571 100644 --- a/back/app/controllers/application_controller.rb +++ b/back/app/controllers/application_controller.rb @@ -159,3 +159,5 @@ def remove_image_if_requested!(resource, resource_params, image_field_name) resource.public_send(:"remove_#{image_field_name}!") end end + +ApplicationController.include(AggressiveCaching::Patches::ApplicationController) diff --git a/back/app/controllers/web_api/v1/app_configurations_controller.rb b/back/app/controllers/web_api/v1/app_configurations_controller.rb index 0b097ff72b83..2de1df37be45 100644 --- a/back/app/controllers/web_api/v1/app_configurations_controller.rb +++ b/back/app/controllers/web_api/v1/app_configurations_controller.rb @@ -43,3 +43,5 @@ def config_params .permit(:logo, :favicon, settings: {}, style: {}) end end + +WebApi::V1::AppConfigurationsController.include(AggressiveCaching::Patches::WebApi::V1::AppConfigurationsController) diff --git a/back/engines/commercial/aggressive_caching/aggressive_caching.gemspec b/back/engines/commercial/aggressive_caching/aggressive_caching.gemspec new file mode 100644 index 000000000000..6fc505511b91 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/aggressive_caching.gemspec @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +$LOAD_PATH.push File.expand_path('lib', __dir__) + +# Maintain your gem's version: +require 'aggressive_caching/version' + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = 'aggressive_caching' + s.version = AggressiveCaching::VERSION + s.summary = 'Optionally enable aggressive caching for high traffic situations' + s.authors = ['CitizenLab'] + s.licenses = [Gem::Licenses::NONSTANDARD] # ['CitizenLab Commercial License V2'] + s.files = Dir['{app,config,db,lib}/**/*', 'Rakefile', 'README.md'] + + s.add_dependency 'rails', '~> 7.0' + s.add_dependency 'actionpack-action_caching', '~> 1.2' + + s.add_development_dependency 'rspec_api_documentation' + s.add_development_dependency 'rspec-rails' +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb new file mode 100644 index 000000000000..c1d7d2759c9c --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb @@ -0,0 +1,21 @@ +module AggressiveCaching + module Patches + module ApplicationController + extend ActiveSupport::Concern + + included do + include ActionController::Caching # Needed to make actionpack-action_caching work with ActionController::API + self.cache_store = Rails.cache + end + + # Needed to make actionpack-action_caching work with ActionController::API. Fake implemenetation of what is normally provided by ActionController::Base + def action_has_layout=(value) + value + end + + def aggressive_caching_enabled? + AppConfiguration.instance.feature_activated?('aggressive_caching') + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb new file mode 100644 index 000000000000..06bd793e5204 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module AppConfigurationsController + def self.included(base) + base.class_eval do + skip_after_action :verify_authorized, only: [:show], if: :aggressive_caching_enabled? + + caches_action :show, expires_in: 1.minute, if: lambda { + aggressive_caching_enabled? && (!current_user || current_user.highest_role == 'user') + } + end + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/lib/aggressive_caching.rb b/back/engines/commercial/aggressive_caching/lib/aggressive_caching.rb new file mode 100644 index 000000000000..2ff82f09aedc --- /dev/null +++ b/back/engines/commercial/aggressive_caching/lib/aggressive_caching.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require 'actionpack/action_caching' +require 'aggressive_caching/engine' + +module AggressiveCaching + # Your code goes here... +end diff --git a/back/engines/commercial/aggressive_caching/lib/aggressive_caching/engine.rb b/back/engines/commercial/aggressive_caching/lib/aggressive_caching/engine.rb new file mode 100644 index 000000000000..f77a92121333 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/lib/aggressive_caching/engine.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module AggressiveCaching + class Engine < ::Rails::Engine + isolate_namespace AggressiveCaching + + config.to_prepare do + require 'aggressive_caching/feature_specification' + AppConfiguration::Settings.add_feature(AggressiveCaching::FeatureSpecification) + end + end +end diff --git a/back/engines/commercial/aggressive_caching/lib/aggressive_caching/feature_specification.rb b/back/engines/commercial/aggressive_caching/lib/aggressive_caching/feature_specification.rb new file mode 100644 index 000000000000..f2db73c32665 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/lib/aggressive_caching/feature_specification.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# Engine namespace +module AggressiveCaching + module FeatureSpecification + # Note that we are extending (not including) here! + extend CitizenLab::Mixins::FeatureSpecification + + # will be used as the property key in the main settings json schema + def self.feature_name + 'aggressive_caching' + end + + def self.feature_title + 'Aggressive caching' + end + + def self.feature_description + <<~DESC + This feature is used to aggressivel cache API responses. + It should always be turned off, unless in exceptional circumstances where we are experiencing major traffic peaks which the platform can no longer handle. + When enabled, some data on the platform might take some time to update after changes have been made.' + DESC + end + + def self.allowed_by_default + true + end + + def self.enabled_by_default + false + end + end +end diff --git a/back/engines/commercial/aggressive_caching/lib/aggressive_caching/version.rb b/back/engines/commercial/aggressive_caching/lib/aggressive_caching/version.rb new file mode 100644 index 000000000000..928fc1d4e0f8 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/lib/aggressive_caching/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module AggressiveCaching + VERSION = '0.1.0' +end From 0a63ebf93ae73a0f6734fd20b9060769ff673165 Mon Sep 17 00:00:00 2001 From: Koen Gremmelprez Date: Fri, 18 Oct 2024 17:34:37 +0200 Subject: [PATCH 2/3] WIP Adding action caching to most important routes --- .../v1/admin_publications_controller.rb | 2 ++ .../web_api/v1/areas_controller.rb | 2 ++ .../web_api/v1/comments_controller.rb | 2 ++ .../web_api/v1/folders_controller.rb | 2 ++ .../web_api/v1/idea_statuses_controller.rb | 2 ++ .../web_api/v1/ideas_controller.rb | 1 + .../web_api/v1/nav_bar_items_controller.rb | 2 ++ .../web_api/v1/projects_controller.rb | 2 ++ .../web_api/v1/static_pages_controller.rb | 2 ++ .../web_api/v1/topics_controller.rb | 2 ++ .../patches/application_controller.rb | 13 +++++-- .../v1/admin_publications_controller.rb | 24 +++++++++++++ .../v1/app_configurations_controller.rb | 12 ++++--- .../patches/web_api/v1/areas_controller.rb | 24 +++++++++++++ .../patches/web_api/v1/comments_controller.rb | 25 +++++++++++++ .../v1/content_builder_layouts_controller.rb | 23 ++++++++++++ .../patches/web_api/v1/folders_controller.rb | 24 +++++++++++++ .../web_api/v1/idea_statuses_controller.rb | 24 +++++++++++++ .../patches/web_api/v1/ideas_controller.rb | 36 +++++++++++++++++++ .../web_api/v1/nav_bar_items_controller.rb | 23 ++++++++++++ .../patches/web_api/v1/projects_controller.rb | 24 +++++++++++++ .../web_api/v1/static_pages_controller.rb | 23 ++++++++++++ .../patches/web_api/v1/topics_controller.rb | 24 +++++++++++++ .../v1/content_builder_layouts_controller.rb | 2 ++ 24 files changed, 313 insertions(+), 7 deletions(-) create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/admin_publications_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/areas_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/comments_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/content_builder_layouts_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/folders_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/idea_statuses_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/ideas_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/nav_bar_items_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/projects_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/static_pages_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/topics_controller.rb diff --git a/back/app/controllers/web_api/v1/admin_publications_controller.rb b/back/app/controllers/web_api/v1/admin_publications_controller.rb index f2b1ab45a247..e6143b74c902 100644 --- a/back/app/controllers/web_api/v1/admin_publications_controller.rb +++ b/back/app/controllers/web_api/v1/admin_publications_controller.rb @@ -69,3 +69,5 @@ def set_admin_publication authorize @publication end end + +WebApi::V1::AdminPublicationsController.include(AggressiveCaching::Patches::WebApi::V1::AdminPublicationsController) diff --git a/back/app/controllers/web_api/v1/areas_controller.rb b/back/app/controllers/web_api/v1/areas_controller.rb index 3ec41e8f5f48..7bb336166f76 100644 --- a/back/app/controllers/web_api/v1/areas_controller.rb +++ b/back/app/controllers/web_api/v1/areas_controller.rb @@ -106,3 +106,5 @@ def set_side_effects_service @side_fx_service = SideFxAreaService.new end end + +WebApi::V1::AreasController.include(AggressiveCaching::Patches::WebApi::V1::AreasController) diff --git a/back/app/controllers/web_api/v1/comments_controller.rb b/back/app/controllers/web_api/v1/comments_controller.rb index 56ac365eee03..33a633f78fca 100644 --- a/back/app/controllers/web_api/v1/comments_controller.rb +++ b/back/app/controllers/web_api/v1/comments_controller.rb @@ -279,3 +279,5 @@ def anonymous_not_allowed? end end end + +WebApi::V1::CommentsController.include(AggressiveCaching::Patches::WebApi::V1::CommentsController) diff --git a/back/app/controllers/web_api/v1/folders_controller.rb b/back/app/controllers/web_api/v1/folders_controller.rb index bb51bf49b2e8..71e66e6ca19b 100644 --- a/back/app/controllers/web_api/v1/folders_controller.rb +++ b/back/app/controllers/web_api/v1/folders_controller.rb @@ -126,3 +126,5 @@ def project_folder_params ) end end + +WebApi::V1::FoldersController.include(AggressiveCaching::Patches::WebApi::V1::FoldersController) diff --git a/back/app/controllers/web_api/v1/idea_statuses_controller.rb b/back/app/controllers/web_api/v1/idea_statuses_controller.rb index 17d85d9f6f89..e3b5efc19465 100644 --- a/back/app/controllers/web_api/v1/idea_statuses_controller.rb +++ b/back/app/controllers/web_api/v1/idea_statuses_controller.rb @@ -114,3 +114,5 @@ def max_ordering IdeaStatus.where(code: IdeaStatus::LOCKED_CODES, participation_method: @idea_status.participation_method).maximum(:ordering) || -1 end end + +WebApi::V1::IdeaStatusesController.include(AggressiveCaching::Patches::WebApi::V1::IdeaStatusesController) diff --git a/back/app/controllers/web_api/v1/ideas_controller.rb b/back/app/controllers/web_api/v1/ideas_controller.rb index a4e4529c3e7f..9fe088e9edde 100644 --- a/back/app/controllers/web_api/v1/ideas_controller.rb +++ b/back/app/controllers/web_api/v1/ideas_controller.rb @@ -477,3 +477,4 @@ def not_allowed_update_errors(input) end WebApi::V1::IdeasController.prepend(IdeaAssignment::Patches::WebApi::V1::IdeasController) +WebApi::V1::IdeasController.include(AggressiveCaching::Patches::WebApi::V1::IdeasController) diff --git a/back/app/controllers/web_api/v1/nav_bar_items_controller.rb b/back/app/controllers/web_api/v1/nav_bar_items_controller.rb index edfa9556ceaa..898b0750ae3b 100644 --- a/back/app/controllers/web_api/v1/nav_bar_items_controller.rb +++ b/back/app/controllers/web_api/v1/nav_bar_items_controller.rb @@ -72,3 +72,5 @@ def set_item authorize @item end end + +WebApi::V1::NavBarItemsController.include(AggressiveCaching::Patches::WebApi::V1::NavBarItemsController) diff --git a/back/app/controllers/web_api/v1/projects_controller.rb b/back/app/controllers/web_api/v1/projects_controller.rb index d15481da5019..7dde0fb5118e 100644 --- a/back/app/controllers/web_api/v1/projects_controller.rb +++ b/back/app/controllers/web_api/v1/projects_controller.rb @@ -215,3 +215,5 @@ def check_publication_inconsistencies! end end end + +WebApi::V1::ProjectsController.include(AggressiveCaching::Patches::WebApi::V1::ProjectsController) diff --git a/back/app/controllers/web_api/v1/static_pages_controller.rb b/back/app/controllers/web_api/v1/static_pages_controller.rb index 53f3290cb3cd..4171cdcff8da 100644 --- a/back/app/controllers/web_api/v1/static_pages_controller.rb +++ b/back/app/controllers/web_api/v1/static_pages_controller.rb @@ -81,3 +81,5 @@ def set_page authorize @page end end + +WebApi::V1::StaticPagesController.include(AggressiveCaching::Patches::WebApi::V1::StaticPagesController) diff --git a/back/app/controllers/web_api/v1/topics_controller.rb b/back/app/controllers/web_api/v1/topics_controller.rb index a697158dcde8..d882e3c0fa1d 100644 --- a/back/app/controllers/web_api/v1/topics_controller.rb +++ b/back/app/controllers/web_api/v1/topics_controller.rb @@ -112,3 +112,5 @@ def set_topic authorize @topic end end + +WebApi::V1::TopicsController.include(AggressiveCaching::Patches::WebApi::V1::TopicsController) diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb index c1d7d2759c9c..75748b5f0fad 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb @@ -4,8 +4,15 @@ module ApplicationController extend ActiveSupport::Concern included do - include ActionController::Caching # Needed to make actionpack-action_caching work with ActionController::API + # Needed to make actionpack-action_caching work with ActionController::API + include ActionController::Caching + # For some Reason, ActionController::Caching is not picking up the Rails + # cache_store by itself. This initialization works, but could probaby be + # improved self.cache_store = Rails.cache + + skip_after_action :verify_policy_scoped, if: :aggressive_caching_active? + skip_after_action :verify_authorized, if: :aggressive_caching_active? end # Needed to make actionpack-action_caching work with ActionController::API. Fake implemenetation of what is normally provided by ActionController::Base @@ -13,7 +20,9 @@ def action_has_layout=(value) value end - def aggressive_caching_enabled? + # This method is typically overriden in the controller to determine for + # which users aggressive caching should be active + def aggressive_caching_active? AppConfiguration.instance.feature_activated?('aggressive_caching') end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/admin_publications_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/admin_publications_controller.rb new file mode 100644 index 000000000000..a86088947f21 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/admin_publications_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module AdminPublicationsController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :show, :status_counts, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb index 06bd793e5204..8bf50da85d65 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb @@ -7,13 +7,15 @@ module V1 module AppConfigurationsController def self.included(base) base.class_eval do - skip_after_action :verify_authorized, only: [:show], if: :aggressive_caching_enabled? - - caches_action :show, expires_in: 1.minute, if: lambda { - aggressive_caching_enabled? && (!current_user || current_user.highest_role == 'user') - } + with_options if: :aggressive_caching_active? do + caches_action :show, expires_in: 1.minute + end end end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/areas_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/areas_controller.rb new file mode 100644 index 000000000000..1851e56cee23 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/areas_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module AreasController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :show, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/comments_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/comments_controller.rb new file mode 100644 index 000000000000..910a5e3e9e61 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/comments_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module CommentsController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, :children, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :show, expires_in: 1.minute + end + end + end + + # We don't cache comments for normal users, since they might post/change + def aggressive_caching_active? + super && !current_user + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/content_builder_layouts_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/content_builder_layouts_controller.rb new file mode 100644 index 000000000000..5dc1be75c7e7 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/content_builder_layouts_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module ContentBuilderLayoutsController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :show, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/folders_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/folders_controller.rb new file mode 100644 index 000000000000..57d6dc5475a3 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/folders_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module FoldersController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :show, :by_slug, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/idea_statuses_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/idea_statuses_controller.rb new file mode 100644 index 000000000000..d14c59009c74 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/idea_statuses_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module IdeaStatusesController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :show, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/ideas_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/ideas_controller.rb new file mode 100644 index 000000000000..141f2553ee30 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/ideas_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module IdeasController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + # We need an extra :skip_after_action here, because the + # IdeasController re-specifies the after_action hook explicitly + # and takes precedence + skip_after_action :verify_policy_scoped + + caches_action :index, :index_mini, :filter_counts, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :show, :by_slug, expires_in: 1.minute + end + caches_action :json_forms_schema, expires_in: 1.day, if: :aggressive_caching_active_and_not_admin? + end + end + + # We don't cache ideas for normal users, since they might post/change ideas + def aggressive_caching_active? + super && !current_user + end + + # But we do cache the json_forms_schema for normal users, since they can't impact it + def aggressive_caching_active_and_not_admin? + AppConfiguration.instance.feature_activated?('aggressive_caching') && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/nav_bar_items_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/nav_bar_items_controller.rb new file mode 100644 index 000000000000..19c729c67222 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/nav_bar_items_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module NavBarItemsController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/projects_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/projects_controller.rb new file mode 100644 index 000000000000..69b47c6e2ea2 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/projects_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module ProjectsController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :show, :by_slug, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/static_pages_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/static_pages_controller.rb new file mode 100644 index 000000000000..391cbe9c91a0 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/static_pages_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module StaticPagesController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, :show, :by_slug, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/topics_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/topics_controller.rb new file mode 100644 index 000000000000..7210b2c341a7 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/topics_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module TopicsController + def self.included(base) + base.class_eval do + with_options if: :aggressive_caching_active? do + caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :show, expires_in: 1.minute + end + end + end + + def aggressive_caching_active? + super && (!current_user || current_user.normal_user?) + end + end + end + end + end +end diff --git a/back/engines/commercial/content_builder/app/controllers/content_builder/web_api/v1/content_builder_layouts_controller.rb b/back/engines/commercial/content_builder/app/controllers/content_builder/web_api/v1/content_builder_layouts_controller.rb index 170e38f1e7c7..7a5ebc1d4456 100644 --- a/back/engines/commercial/content_builder/app/controllers/content_builder/web_api/v1/content_builder_layouts_controller.rb +++ b/back/engines/commercial/content_builder/app/controllers/content_builder/web_api/v1/content_builder_layouts_controller.rb @@ -106,3 +106,5 @@ def to_boolean(value) end end end + +ContentBuilder::WebApi::V1::ContentBuilderLayoutsController.include(AggressiveCaching::Patches::WebApi::V1::ContentBuilderLayoutsController) From 4814e843a8d0c52f315ae4f8834cbc27b6a24b86 Mon Sep 17 00:00:00 2001 From: Koen Gremmelprez Date: Fri, 18 Oct 2024 23:33:17 +0200 Subject: [PATCH 3/3] Added more cached controllers and fixed too aggressive caching issue --- .../web_api/v1/events_controller.rb | 2 + .../v1/official_feedback_controller.rb | 2 + .../v1/phase_custom_fields_controller.rb | 2 + .../web_api/v1/phases_controller.rb | 2 + .../v1/project_custom_fields_controller.rb | 2 + back/config/environments/test.rb | 2 +- .../patches/application_controller.rb | 19 +++- .../v1/admin_publications_controller.rb | 9 +- .../v1/app_configurations_controller.rb | 6 +- .../patches/web_api/v1/areas_controller.rb | 8 +- .../patches/web_api/v1/comments_controller.rb | 9 +- .../v1/content_builder_layouts_controller.rb | 6 +- .../patches/web_api/v1/events_controller.rb | 20 ++++ .../patches/web_api/v1/folders_controller.rb | 8 +- .../web_api/v1/idea_statuses_controller.rb | 8 +- .../patches/web_api/v1/ideas_controller.rb | 16 +-- .../web_api/v1/nav_bar_items_controller.rb | 9 +- .../v1/official_feedback_controller.rb | 20 ++++ .../v1/phase_custom_fields_controller.rb | 19 ++++ .../patches/web_api/v1/phases_controller.rb | 20 ++++ .../v1/project_custom_fields_controller.rb | 19 ++++ .../patches/web_api/v1/projects_controller.rb | 8 +- .../web_api/v1/static_pages_controller.rb | 9 +- .../patches/web_api/v1/topics_controller.rb | 8 +- .../spec/acceptance/ideas_spec.rb | 86 ++++++++++++++++ .../spec/acceptance/static_pages_spec.rb | 22 +++++ .../spec/acceptance/topics_spec.rb | 97 +++++++++++++++++++ back/spec/spec_helper.rb | 8 ++ 28 files changed, 365 insertions(+), 81 deletions(-) create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/events_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/official_feedback_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/phase_custom_fields_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/phases_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/project_custom_fields_controller.rb create mode 100644 back/engines/commercial/aggressive_caching/spec/acceptance/ideas_spec.rb create mode 100644 back/engines/commercial/aggressive_caching/spec/acceptance/static_pages_spec.rb create mode 100644 back/engines/commercial/aggressive_caching/spec/acceptance/topics_spec.rb diff --git a/back/app/controllers/web_api/v1/events_controller.rb b/back/app/controllers/web_api/v1/events_controller.rb index 77682ca952dc..2d34e8dd5f19 100644 --- a/back/app/controllers/web_api/v1/events_controller.rb +++ b/back/app/controllers/web_api/v1/events_controller.rb @@ -192,3 +192,5 @@ def sidefx @sidefx ||= SideFxEventService.new end end + +WebApi::V1::EventsController.include(AggressiveCaching::Patches::WebApi::V1::EventsController) diff --git a/back/app/controllers/web_api/v1/official_feedback_controller.rb b/back/app/controllers/web_api/v1/official_feedback_controller.rb index c1f17c0ffb57..6a52cffb7498 100644 --- a/back/app/controllers/web_api/v1/official_feedback_controller.rb +++ b/back/app/controllers/web_api/v1/official_feedback_controller.rb @@ -97,3 +97,5 @@ def official_feedback_params ) end end + +WebApi::V1::OfficialFeedbackController.include(AggressiveCaching::Patches::WebApi::V1::OfficialFeedbackController) diff --git a/back/app/controllers/web_api/v1/phase_custom_fields_controller.rb b/back/app/controllers/web_api/v1/phase_custom_fields_controller.rb index 399673bb77a2..b804767c491e 100644 --- a/back/app/controllers/web_api/v1/phase_custom_fields_controller.rb +++ b/back/app/controllers/web_api/v1/phase_custom_fields_controller.rb @@ -23,3 +23,5 @@ def custom_fields IdeaCustomFieldsService.new(phase.pmethod.custom_form).enabled_fields_with_other_options end end + +WebApi::V1::PhaseCustomFieldsController.include(AggressiveCaching::Patches::WebApi::V1::PhaseCustomFieldsController) diff --git a/back/app/controllers/web_api/v1/phases_controller.rb b/back/app/controllers/web_api/v1/phases_controller.rb index dad8c0461031..c548905e3dea 100644 --- a/back/app/controllers/web_api/v1/phases_controller.rb +++ b/back/app/controllers/web_api/v1/phases_controller.rb @@ -173,3 +173,5 @@ def detect_invalid_timeline_changes end end end + +WebApi::V1::PhasesController.include(AggressiveCaching::Patches::WebApi::V1::PhasesController) diff --git a/back/app/controllers/web_api/v1/project_custom_fields_controller.rb b/back/app/controllers/web_api/v1/project_custom_fields_controller.rb index 89660d9d22fa..4b64b3b1f633 100644 --- a/back/app/controllers/web_api/v1/project_custom_fields_controller.rb +++ b/back/app/controllers/web_api/v1/project_custom_fields_controller.rb @@ -27,3 +27,5 @@ def custom_fields IdeaCustomFieldsService.new(phase.pmethod.custom_form).enabled_fields end end + +WebApi::V1::ProjectCustomFieldsController.include(AggressiveCaching::Patches::WebApi::V1::ProjectCustomFieldsController) diff --git a/back/config/environments/test.rb b/back/config/environments/test.rb index 5993ccf69db5..4dd8787f84ef 100644 --- a/back/config/environments/test.rb +++ b/back/config/environments/test.rb @@ -34,7 +34,7 @@ config.action_controller.perform_caching = false # Caching must be turned on for Rack::Attack to work and Rack::Attack tests to pass. - # config.cache_store = :null_store + config.cache_store = :memory_store # Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb index 75748b5f0fad..66a9fc711ab5 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/application_controller.rb @@ -20,11 +20,26 @@ def action_has_layout=(value) value end - # This method is typically overriden in the controller to determine for - # which users aggressive caching should be active def aggressive_caching_active? AppConfiguration.instance.feature_activated?('aggressive_caching') end + + # Helpers for the subclasses to determinte for whom to cache + def caching_and_visitor? + aggressive_caching_active? && current_user.nil? + end + + def caching_and_non_admin? + aggressive_caching_active? && (current_user.nil? || current_user.normal_user?) + end + + # Quite some API responses embed data about whether the current user + # follows the returned resource. This lets us still cache those responses + # for users that are not following anything + def caching_and_not_following? + aggressive_caching_active? && + (current_user.nil? || (current_user.normal_user? && current_user&.follows&.none?)) + end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/admin_publications_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/admin_publications_controller.rb index a86088947f21..9ad24b62647b 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/admin_publications_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/admin_publications_controller.rb @@ -7,16 +7,13 @@ module V1 module AdminPublicationsController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + # We can only cache for visitors, because permissions play a role in the admin publications shown + with_options if: :caching_and_visitor? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } caches_action :show, :status_counts, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb index 8bf50da85d65..b27bbd04d202 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/app_configurations_controller.rb @@ -7,15 +7,11 @@ module V1 module AppConfigurationsController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do + with_options if: :caching_and_non_admin? do caches_action :show, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/areas_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/areas_controller.rb index 1851e56cee23..0cc5d60fd1b2 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/areas_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/areas_controller.rb @@ -7,16 +7,12 @@ module V1 module AreasController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + with_options if: :caching_and_not_following? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } caches_action :show, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/comments_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/comments_controller.rb index 910a5e3e9e61..b19992144abb 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/comments_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/comments_controller.rb @@ -7,17 +7,12 @@ module V1 module CommentsController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, :children, expires_in: 1.minute, cache_path: -> { params.to_s } + with_options if: :caching_and_visitor? do + caches_action :index, :children, expires_in: 1.minute, cache_path: -> { request.query_parameters } caches_action :show, expires_in: 1.minute end end end - - # We don't cache comments for normal users, since they might post/change - def aggressive_caching_active? - super && !current_user - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/content_builder_layouts_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/content_builder_layouts_controller.rb index 5dc1be75c7e7..d34e2faafd43 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/content_builder_layouts_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/content_builder_layouts_controller.rb @@ -7,15 +7,11 @@ module V1 module ContentBuilderLayoutsController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do + with_options if: :caching_and_non_admin? do caches_action :show, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/events_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/events_controller.rb new file mode 100644 index 000000000000..5176cd9a0143 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/events_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module EventsController + def self.included(base) + base.class_eval do + with_options if: :caching_and_visitor? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } + caches_action :show, expires_in: 1.minute + end + end + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/folders_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/folders_controller.rb index 57d6dc5475a3..d7e84df001ab 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/folders_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/folders_controller.rb @@ -7,16 +7,12 @@ module V1 module FoldersController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + with_options if: :caching_and_visitor? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } caches_action :show, :by_slug, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/idea_statuses_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/idea_statuses_controller.rb index d14c59009c74..79f97abd9c08 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/idea_statuses_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/idea_statuses_controller.rb @@ -7,16 +7,12 @@ module V1 module IdeaStatusesController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + with_options if: :caching_and_non_admin? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } caches_action :show, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/ideas_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/ideas_controller.rb index 141f2553ee30..956535682295 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/ideas_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/ideas_controller.rb @@ -7,28 +7,18 @@ module V1 module IdeasController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do + with_options if: :caching_and_visitor? do # We need an extra :skip_after_action here, because the # IdeasController re-specifies the after_action hook explicitly # and takes precedence skip_after_action :verify_policy_scoped - caches_action :index, :index_mini, :filter_counts, expires_in: 1.minute, cache_path: -> { params.to_s } + caches_action :index, :index_mini, :filter_counts, :as_markers, expires_in: 1.minute, cache_path: -> { request.query_parameters } caches_action :show, :by_slug, expires_in: 1.minute end - caches_action :json_forms_schema, expires_in: 1.day, if: :aggressive_caching_active_and_not_admin? + caches_action :json_forms_schema, expires_in: 1.day, if: :caching_and_non_admin? end end - - # We don't cache ideas for normal users, since they might post/change ideas - def aggressive_caching_active? - super && !current_user - end - - # But we do cache the json_forms_schema for normal users, since they can't impact it - def aggressive_caching_active_and_not_admin? - AppConfiguration.instance.feature_activated?('aggressive_caching') && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/nav_bar_items_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/nav_bar_items_controller.rb index 19c729c67222..d042c3b65d35 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/nav_bar_items_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/nav_bar_items_controller.rb @@ -7,15 +7,12 @@ module V1 module NavBarItemsController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, expires_in: 1.minute + with_options if: :caching_and_non_admin? do + skip_after_action :verify_policy_scoped + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/official_feedback_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/official_feedback_controller.rb new file mode 100644 index 000000000000..c4a4809d2c22 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/official_feedback_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module OfficialFeedbackController + def self.included(base) + base.class_eval do + with_options if: :caching_and_visitor? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } + caches_action :show, expires_in: 1.minute + end + end + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/phase_custom_fields_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/phase_custom_fields_controller.rb new file mode 100644 index 000000000000..a7c3d10c29f8 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/phase_custom_fields_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module PhaseCustomFieldsController + def self.included(base) + base.class_eval do + with_options if: :caching_and_visitor? do + caches_action :json_forms_schema, expires_in: 1.minute + end + end + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/phases_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/phases_controller.rb new file mode 100644 index 000000000000..228281849ff0 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/phases_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module PhasesController + def self.included(base) + base.class_eval do + with_options if: :caching_and_visitor? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } + caches_action :show, :submission_count, expires_in: 1.minute + end + end + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/project_custom_fields_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/project_custom_fields_controller.rb new file mode 100644 index 000000000000..6a8d546c95ed --- /dev/null +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/project_custom_fields_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module AggressiveCaching + module Patches + module WebApi + module V1 + module ProjectCustomFieldsController + def self.included(base) + base.class_eval do + with_options if: :caching_and_visitor? do + caches_action :json_forms_schema, expires_in: 1.minute + end + end + end + end + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/projects_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/projects_controller.rb index 69b47c6e2ea2..ebc34fd9873c 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/projects_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/projects_controller.rb @@ -7,16 +7,12 @@ module V1 module ProjectsController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + with_options if: :caching_and_visitor? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } caches_action :show, :by_slug, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/static_pages_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/static_pages_controller.rb index 391cbe9c91a0..7312902e85f3 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/static_pages_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/static_pages_controller.rb @@ -7,15 +7,12 @@ module V1 module StaticPagesController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, :show, :by_slug, expires_in: 1.minute + with_options if: :caching_and_non_admin? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } + caches_action :show, :by_slug, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/topics_controller.rb b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/topics_controller.rb index 7210b2c341a7..abb34b167ec1 100644 --- a/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/topics_controller.rb +++ b/back/engines/commercial/aggressive_caching/app/controllers/aggressive_caching/patches/web_api/v1/topics_controller.rb @@ -7,16 +7,12 @@ module V1 module TopicsController def self.included(base) base.class_eval do - with_options if: :aggressive_caching_active? do - caches_action :index, expires_in: 1.minute, cache_path: -> { params.to_s } + with_options if: :caching_and_not_following? do + caches_action :index, expires_in: 1.minute, cache_path: -> { request.query_parameters } caches_action :show, expires_in: 1.minute end end end - - def aggressive_caching_active? - super && (!current_user || current_user.normal_user?) - end end end end diff --git a/back/engines/commercial/aggressive_caching/spec/acceptance/ideas_spec.rb b/back/engines/commercial/aggressive_caching/spec/acceptance/ideas_spec.rb new file mode 100644 index 000000000000..cf4262cb6985 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/spec/acceptance/ideas_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'rspec_api_documentation/dsl' + +resource 'Ideas', :clear_cache, document: false do + before do + header 'Content-Type', 'application/json' + settings = AppConfiguration.instance.settings + settings['aggressive_caching'] = { 'enabled' => true, 'allowed' => true } + AppConfiguration.instance.update!(settings:) + end + + get 'web_api/v1/ideas' do + example 'caches for a visitor' do + expect(Rails.cache.read('views/example.org/web_api/v1/ideas.json')).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read('views/example.org/web_api/v1/ideas.json')).to be_present + end + + context 'when logged in' do + before { header_token_for create(:user) } + + example 'does not cache' do + expect(Rails.cache.read('views/example.org/web_api/v1/ideas.json')).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read('views/example.org/web_api/v1/ideas.json')).to be_nil + end + end + + context 'with aggressive caching disabled' do + before do + settings = AppConfiguration.instance.settings + settings['aggressive_caching'] = { 'enabled' => false, 'allowed' => true } + AppConfiguration.instance.update!(settings:) + end + + example 'does not cache' do + expect(Rails.cache.read('views/example.org/web_api/v1/ideas.json')).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read('views/example.org/web_api/v1/ideas.json')).to be_nil + end + end + end + + get 'web_api/v1/ideas/:id' do + let(:idea) { create(:idea) } + let(:id) { idea.id } + + example 'caches for a visitor' do + expect(Rails.cache.read("views/example.org/web_api/v1/ideas/#{id}.json")).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read("views/example.org/web_api/v1/ideas/#{id}.json")).to be_present + end + + context 'when logged in' do + before { header_token_for create(:user) } + + example 'does not cache' do + expect(Rails.cache.read("views/example.org/web_api/v1/ideas/#{id}.json")).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read("views/example.org/web_api/v1/ideas/#{id}.json")).to be_nil + end + end + + context 'with aggressive caching disabled' do + before do + settings = AppConfiguration.instance.settings + settings['aggressive_caching'] = { 'enabled' => false, 'allowed' => true } + AppConfiguration.instance.update!(settings:) + end + + example 'does not cache' do + expect(Rails.cache.read("views/example.org/web_api/v1/ideas/#{id}.json")).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read("views/example.org/web_api/v1/ideas/#{id}.json")).to be_nil + end + end + end +end diff --git a/back/engines/commercial/aggressive_caching/spec/acceptance/static_pages_spec.rb b/back/engines/commercial/aggressive_caching/spec/acceptance/static_pages_spec.rb new file mode 100644 index 000000000000..72e341628155 --- /dev/null +++ b/back/engines/commercial/aggressive_caching/spec/acceptance/static_pages_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'rspec_api_documentation/dsl' + +resource 'StaticPages', :clear_cache, document: false do + before do + header 'Content-Type', 'application/json' + settings = AppConfiguration.instance.settings + settings['aggressive_caching'] = { 'enabled' => true, 'allowed' => true } + AppConfiguration.instance.update!(settings:) + end + + get 'web_api/v1/static_pages' do + example 'Caches for a visitor' do + expect(Rails.cache.read('views/example.org/web_api/v1/static_pages.json')).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read('views/example.org/web_api/v1/static_pages.json')).to be_present + end + end +end diff --git a/back/engines/commercial/aggressive_caching/spec/acceptance/topics_spec.rb b/back/engines/commercial/aggressive_caching/spec/acceptance/topics_spec.rb new file mode 100644 index 000000000000..2529a54e4d9d --- /dev/null +++ b/back/engines/commercial/aggressive_caching/spec/acceptance/topics_spec.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'rspec_api_documentation/dsl' + +resource 'Topics', :clear_cache, document: false do + before do + header 'Content-Type', 'application/json' + settings = AppConfiguration.instance.settings + settings['aggressive_caching'] = { 'enabled' => true, 'allowed' => true } + AppConfiguration.instance.update!(settings:) + end + + get 'web_api/v1/topics' do + example 'caches for a visitor' do + expect(Rails.cache.read('views/example.org/web_api/v1/topics.json')).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read('views/example.org/web_api/v1/topics.json')).to be_present + end + + context 'when logged in and following something' do + before { header_token_for create(:follower).user } + + example 'it does not cache' do + expect(Rails.cache.read('views/example.org/web_api/v1/topics.json')).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read('views/example.org/web_api/v1/topics.json')).to be_nil + end + end + + context 'when logged in and not following anything' do + before { header_token_for create(:user) } + + example 'it caches' do + expect(Rails.cache.read('views/example.org/web_api/v1/topics.json')).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read('views/example.org/web_api/v1/topics.json')).to be_present + end + end + + context 'with aggressive caching disabled' do + before do + settings = AppConfiguration.instance.settings + settings['aggressive_caching'] = { 'enabled' => false, 'allowed' => true } + AppConfiguration.instance.update!(settings:) + end + + example 'does not cache' do + expect(Rails.cache.read('views/example.org/web_api/v1/topics.json')).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read('views/example.org/web_api/v1/topics.json')).to be_nil + end + end + end + + get 'web_api/v1/topics/:id' do + let(:topic) { create(:topic) } + let(:id) { topic.id } + + example 'caches for a visitor' do + expect(Rails.cache.read("views/example.org/web_api/v1/topics/#{id}.json")).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read("views/example.org/web_api/v1/topics/#{id}.json")).to be_present + end + + context 'when logged in and not following' do + before { header_token_for create(:user) } + + example 'it caches' do + expect(Rails.cache.read("views/example.org/web_api/v1/topics/#{id}.json")).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read("views/example.org/web_api/v1/topics/#{id}.json")).to be_present + end + end + + context 'with aggressive caching disabled' do + before do + settings = AppConfiguration.instance.settings + settings['aggressive_caching'] = { 'enabled' => false, 'allowed' => true } + AppConfiguration.instance.update!(settings:) + end + + example 'does not cache' do + expect(Rails.cache.read("views/example.org/web_api/v1/topics/#{id}.json")).to be_nil + do_request + expect(status).to eq 200 + expect(Rails.cache.read("views/example.org/web_api/v1/topics/#{id}.json")).to be_nil + end + end + end +end diff --git a/back/spec/spec_helper.rb b/back/spec/spec_helper.rb index 2c0a893c519b..3eb5423cf19d 100644 --- a/back/spec/spec_helper.rb +++ b/back/spec/spec_helper.rb @@ -222,6 +222,14 @@ # By default, skip the slow tests and template tests. Can be overriden on the command line. config.filter_run_excluding template_test: true + + config.before(:example, clear_cache: true) do + Rails.cache.clear + end + + config.after(:example, clear_cache: true) do + Rails.cache.clear + end end RSpec::Matchers.define_negated_matcher :not_change, :change