Skip to content

Commit

Permalink
Add ability to ask external caches to invalidate.
Browse files Browse the repository at this point in the history
Add a cached_urls method to the Comment, FoiAttachment, InfoRequest,
PublicBody and User models, containing the entries that those models
wish to invalidate when an event is logged via log_event or when the
censor rules change. Entries are either a fixed path to be purged or
a regex beginning with '^' to be banned.

Adds a NotifyCacheJob that for each of the object's cached_urls, for
each locale, for each host in VARNISH_HOSTS, makes BAN or PURGE HTTP
requests to the host, to invalidate the relevant entries.

TODO: www.whatdotheyknow.com is currently hard-coded in the job.
  • Loading branch information
dracos committed Sep 8, 2023
1 parent 9c2d9b6 commit 7bda1a8
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 0 deletions.
50 changes: 50 additions & 0 deletions app/jobs/notify_cache_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'net/http'

class Net::HTTP::Purge < Net::HTTP::Get
METHOD = 'PURGE'
end

class Net::HTTP::Ban < Net::HTTP::Get
METHOD = 'BAN'
end

##
# Job to notify a cache of URLs to be purged or banned, given an object
# (that must have a cached_urls method).
#
# Examples:
# NotifyCacheJob.perform(InfoRequest.first)
# NotifyCacheJob.perform(FoiAttachment.first)
# NotifyCacheJob.perform(Comment.first)
#
class NotifyCacheJob < ApplicationJob
queue_as :default

def perform(object)
urls = object.cached_urls
locales = [''] + AlaveteliLocalization.available_locales.map { |locale| '/' + locale }

Check warning on line 25 in app/jobs/notify_cache_job.rb

View workflow job for this annotation

GitHub Actions / build

[rubocop] reported by reviewdog 🐶 [Correctable] Layout/LineLength: Line is too long. [90/80] (https://rubystyle.guide#max-line-length) Raw Output: app/jobs/notify_cache_job.rb:25:81: C: [Correctable] Layout/LineLength: Line is too long. [90/80] (https://rubystyle.guide#max-line-length) locales = [''] + AlaveteliLocalization.available_locales.map { |locale| '/' + locale } ^^^^^^^^^^
hosts = AlaveteliConfiguration.varnish_hosts
locales.each do |locale|

Check warning on line 27 in app/jobs/notify_cache_job.rb

View workflow job for this annotation

GitHub Actions / build

[rubocop] reported by reviewdog 🐶 [Correctable] Lint/UnusedBlockArgument: Unused block argument - locale. You can omit the argument if you don't care about it. (https://rubystyle.guide#underscore-unused-vars) Raw Output: app/jobs/notify_cache_job.rb:27:22: W: [Correctable] Lint/UnusedBlockArgument: Unused block argument - locale. You can omit the argument if you don't care about it. (https://rubystyle.guide#underscore-unused-vars) locales.each do |locale| ^^^^^^
hosts.each do |host|
Net::HTTP.start('www.whatdotheyknow.com', 80, host, 6081) do |http|
urls.each do |url|
if url.include? '^'
request = Net::HTTP::Ban.new(url)
else
request = Net::HTTP::Purge.new(url)
end
response = http.request(request)
result = response.code
if result == "200"
Rails.logger.debug("PURGE: Purged URL #{url} at #{host}: #{result}")

Check warning on line 39 in app/jobs/notify_cache_job.rb

View workflow job for this annotation

GitHub Actions / build

[rubocop] reported by reviewdog 🐶 Layout/LineLength: Line is too long. [82/80] (https://rubystyle.guide#max-line-length) Raw Output: app/jobs/notify_cache_job.rb:39:81: C: Layout/LineLength: Line is too long. [82/80] (https://rubystyle.guide#max-line-length) Rails.logger.debug("PURGE: Purged URL #{url} at #{host}: #{result}") ^^
else
Rails.logger.warn(
"PURGE: Unable to purge URL #{url} at #{host}: status #{result}"
)
end
end
end
end
end
end
end
1 change: 1 addition & 0 deletions app/models/censor_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def is_global?
def expire_requests
if info_request
InfoRequestExpireJob.perform_later(info_request)
NotifyCacheJob.perform_later(info_request)
elsif user
InfoRequestExpireJob.perform_later(user, :info_requests)
elsif public_body
Expand Down
7 changes: 7 additions & 0 deletions app/models/comment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ def hide(editor:)
end
end

def cached_urls
[
request_path(info_request),
show_user_wall_path(url_name: user.url_name)
]
end

private

def check_body_has_content
Expand Down
6 changes: 6 additions & 0 deletions app/models/foi_attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,12 @@ def body_as_html(dir, opts = {})
AttachmentToHTML.to_html(self, to_html_opts)
end

def cached_urls
[
request_path(incoming_message.info_request)
]
end

private

def text_type?
Expand Down
38 changes: 38 additions & 0 deletions app/models/info_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class InfoRequest < ApplicationRecord
include InfoRequest::TitleValidation
include Taggable
include Notable
include LinkToHelper

admin_columns exclude: %i[title url_title],
include: %i[rejected_incoming_count]
Expand Down Expand Up @@ -1163,6 +1164,15 @@ def log_event(type, params, options = {})
if !last_event_time || (event.created_at > last_event_time)
update_column(:last_event_time, event.created_at)
end
if AlaveteliConfiguration.varnish_hosts.present?
if %w[comment edit_comment hide_comment].include? type
NotifyCacheJob.perform_later(params.comment)
elsif type == 'edit_attachment'
NotifyCacheJob.perform_later(params.attachment)
else
NotifyCacheJob.perform_later(self)
end
end
event
end

Expand Down Expand Up @@ -1758,6 +1768,34 @@ def latest_refusals
incoming_messages.select(&:refusals?).last&.refusals || []
end

def cached_urls
feed_request = TrackThing.new(
info_request: self,
track_type: 'request_updates'
)
feed_body = TrackThing.new(
public_body: public_body,
track_type: 'public_body_updates'
)
feed_user = TrackThing.new(
tracked_user: user,
track_type: 'user_updates'
)
[
'/',
public_body_path(public_body),
request_path(self),
request_details_path(self),
'^/list*',
do_track_path(feed_request, feed='feed'),

Check warning on line 1790 in app/models/info_request.rb

View workflow job for this annotation

GitHub Actions / build

[rubocop] reported by reviewdog 🐶 [Correctable] Layout/SpaceAroundOperators: Surrounding space missing for operator =. (https://rubystyle.guide#spaces-operators) Raw Output: app/models/info_request.rb:1790:39: C: [Correctable] Layout/SpaceAroundOperators: Surrounding space missing for operator =. (https://rubystyle.guide#spaces-operators) do_track_path(feed_request, feed='feed'), ^
'^/feed/list/*',
do_track_path(feed_body, feed='feed'),

Check warning on line 1792 in app/models/info_request.rb

View workflow job for this annotation

GitHub Actions / build

[rubocop] reported by reviewdog 🐶 [Correctable] Layout/SpaceAroundOperators: Surrounding space missing for operator =. (https://rubystyle.guide#spaces-operators) Raw Output: app/models/info_request.rb:1792:36: C: [Correctable] Layout/SpaceAroundOperators: Surrounding space missing for operator =. (https://rubystyle.guide#spaces-operators) do_track_path(feed_body, feed='feed'), ^
do_track_path(feed_user, feed='feed'),

Check warning on line 1793 in app/models/info_request.rb

View workflow job for this annotation

GitHub Actions / build

[rubocop] reported by reviewdog 🐶 [Correctable] Layout/SpaceAroundOperators: Surrounding space missing for operator =. (https://rubystyle.guide#spaces-operators) Raw Output: app/models/info_request.rb:1793:36: C: [Correctable] Layout/SpaceAroundOperators: Surrounding space missing for operator =. (https://rubystyle.guide#spaces-operators) do_track_path(feed_user, feed='feed'), ^
user_path(user),
show_user_wall_path(url_name: user.url_name)
]
end

private

def self.add_conditions_from_extra_params(params, extra_params)
Expand Down
9 changes: 9 additions & 0 deletions app/models/public_body.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
class PublicBody < ApplicationRecord
include Taggable
include Notable
include LinkToHelper

class ImportCSVDryRun < StandardError; end

Expand Down Expand Up @@ -911,6 +912,14 @@ def questions
PublicBodyQuestion.fetch(self)
end

def cached_urls
[
public_body_path(self),
list_public_bodies_path,
'^/body/list'
]
end

private

# If the url_name has changed, then all requested_from: queries will break
Expand Down
6 changes: 6 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,12 @@ def flipper_id
"User;#{id}"
end

def cached_urls
[
user_path(self)
]
end

private

def set_defaults
Expand Down
14 changes: 14 additions & 0 deletions config/general.yml-example
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,20 @@ EXCEPTION_NOTIFICATIONS_TO:
# ---
MAX_REQUESTS_PER_USER_PER_DAY: 6

# If you're running behind Varnish set this to work out where to send purge
# requests. Otherwise, don't set it.
#
# VARNISH_HOSTS - Array of Strings (default: nil)
#
# Examples:
#
# VARNISH_HOSTS:
# - host1
# - host2
#
# ---
VARNISH_HOSTS: null

# Adding a value here will enable Google Analytics on all non-admin pages for
# non-admin users.
#
Expand Down
1 change: 1 addition & 0 deletions lib/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ module AlaveteliConfiguration
USE_MAILCATCHER_IN_DEVELOPMENT: true,
USER_SIGN_IN_ACTIVITY_RETENTION_DAYS: 0,
UTILITY_SEARCH_PATH: ['/usr/bin', '/usr/local/bin'],
VARNISH_HOSTS: [],
WORKING_OR_CALENDAR_DAYS: 'working'
}
# rubocop:enable Layout/LineLength
Expand Down

0 comments on commit 7bda1a8

Please sign in to comment.