Skip to content

Commit

Permalink
close #2058 invalidate cache for frequently use API (#2059)
Browse files Browse the repository at this point in the history
  • Loading branch information
theachoem authored Nov 20, 2024
1 parent d0b0669 commit fe5aca0
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 1 deletion.
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ AWS_CF_PUBLIC_KEY_ID="KKC****QFJ2"
AWS_OUTPUT_BUCKET_NAME="output-production-cm"

AWS_CF_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----"
-----END PRIVATE KEY-----"

# for GET request with following host, it will be cached.
CONTENT_HOST_URL=https://content.bookme.plus
CONTENT_CACHE_MAX_AGE=7200
20 changes: 20 additions & 0 deletions app/controllers/concerns/spree_cm_commissioner/content_cachable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module SpreeCmCommissioner
module ContentCachable
extend ActiveSupport::Concern

included do
after_action :set_cache_control_for_cdn
end

def max_age
ENV.fetch('CONTENT_CACHE_MAX_AGE', '7200')
end

def set_cache_control_for_cdn
return unless request.get? || request.head?
return unless request.base_url == ENV['CONTENT_HOST_URL']

response.set_header('Cache-Control', "public, max-age=#{max_age}")
end
end
end
23 changes: 23 additions & 0 deletions app/controllers/spree/admin/base_controller_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ def parse_date(date, format: nil)
nil
end

# POST
def invalidate_api_caches
if params[:model].present?
api_patterns_map = {
'SpreeCmCommissioner::HomepageSection' => '/api/v2/storefront/homepage/*',
'SpreeCmCommissioner::HomepageBackground' => '/api/v2/storefront/homepage/*',
'Spree::Menu' => '/api/v2/storefront/menus*'
}

api_patterns = api_patterns_map[params[:model]]

if api_patterns.is_a?(Array)
api_patterns.each { |pattern| SpreeCmCommissioner::InvalidateCacheRequestJob.perform_later(pattern) }
elsif api_patterns.is_a?(String)
SpreeCmCommissioner::InvalidateCacheRequestJob.perform_later(api_patterns)
end
elsif params[:api_pattern].present?
SpreeCmCommissioner::InvalidateCacheRequestJob.perform_later(params[:api_pattern])
end

redirect_back fallback_location: admin_root_path
end

private

def redirect_to_billing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module ActionController
module ApiDecorator
def self.prepended(base)
base.include SpreeCmCommissioner::ExceptionNotificable
base.include SpreeCmCommissioner::ContentCachable
end

# Annonymous block: https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Naming/BlockForwarding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module SpreeCmCommissioner
module ApplicationControllerDecorator
def self.prepended(base)
base.include SpreeCmCommissioner::ExceptionNotificable
base.include SpreeCmCommissioner::ContentCachable
end

# Annonymous block: https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Naming/BlockForwarding
Expand Down
31 changes: 31 additions & 0 deletions app/interactors/spree_cm_commissioner/invalidate_cache_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'aws-sdk-cloudfront'

# pattern: '/api/v2/storefront/homepage_sections*'
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/invalidation-access-logs.html

module SpreeCmCommissioner
class InvalidateCacheRequest < BaseInteractor
delegate :pattern, to: :context

def call
client = ::Aws::CloudFront::Client.new(
region: ENV.fetch('AWS_REGION'),
access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'),
secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY'),
http_open_timeout: 15,
http_read_timeout: 60
)

context.response = client.create_invalidation(
distribution_id: ENV.fetch('ASSETS_SYNC_CF_DIST_ID'),
invalidation_batch: {
caller_reference: Time.now.to_i.to_s,
paths: {
quantity: 1,
items: [pattern]
}
}
)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module SpreeCmCommissioner
class InvalidateCacheRequestJob < ApplicationJob
def perform(pattern)
SpreeCmCommissioner::InvalidateCacheRequest.call(pattern: pattern)
end
end
end
1 change: 1 addition & 0 deletions app/views/spree/admin/homepage_section/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<% end %>

<% content_for :page_actions do %>
<%= button_link_to Spree.t(:clear_cache), admin_invalidate_api_caches_path(model: SpreeCmCommissioner::HomepageSection.name), method: :post, class: "btn btn-outline-primary" %>
<%= button_link_to Spree.t(:new_homepage_section), new_admin_homepage_feed_homepage_section_path, { class: "btn-success", icon: 'add.svg', id: 'admin_new_homepage_section' } %>
<% end %>

Expand Down
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Spree::Core::Engine.add_routes do
# Add your extension routes here
namespace :admin do
post '/invalidate_api_caches', to: 'base#invalidate_api_caches'

resources :promotions do
resources :custom_dates_rules, controller: :promotion_custom_dates_rules, only: %i[edit update] do
member do
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require 'spec_helper'

RSpec.describe SpreeCmCommissioner::InvalidateCacheRequest do
before do
ENV['AWS_REGION'] = 'ap-southeast-1'
ENV['AWS_ACCESS_KEY_ID'] = 'AXXXXXXXXXXXXXXXY'
ENV['AWS_SECRET_ACCESS_KEY'] = 'VO103FAKEFAKEFAKE3020X=I230x1'
ENV['ASSETS_SYNC_CF_DIST_ID'] = 'D12FAKEFAKE'
end

describe '.call', :vcr do
it 'requested to invalidate cache & return response' do
context = described_class.call(pattern: '/staging/422.html')

expect(context.response.location).to eq "https://cloudfront.amazonaws.com/2020-05-31/distribution/D12FAKEFAKE/invalidation/I2PGO1F2LAWEKXXYAKMN0P38N1"
expect(context.response.invalidation.id).to eq "I2PGO1F2LAWEKXXYAKMN0P38N1"
expect(context.response.invalidation.status).to eq "InProgress"
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'spec_helper'

RSpec.describe SpreeCmCommissioner::InvalidateCacheRequestJob do
describe '#perform' do
it 'invokes StateUpdater.call' do
expect(SpreeCmCommissioner::InvalidateCacheRequest).to receive(:call).with(pattern: '/api/storefront/*')
described_class.new.perform('/api/storefront/*')
end
end
end

0 comments on commit fe5aca0

Please sign in to comment.