Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/graphiti/resource/interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ module Interface
extend ActiveSupport::Concern

class_methods do
def cache_resource(expires_in: false)
def cache_resource(expires_in: false, tag: nil)
@cache_resource = true
@cache_expires_in = expires_in
@cache_tag = tag
end

def all(params = {}, base_scope = nil)
Expand Down Expand Up @@ -55,7 +56,7 @@ def build(params, base_scope = nil)
private

def caching_options
{cache: @cache_resource, cache_expires_in: @cache_expires_in}
{cache: @cache_resource, cache_expires_in: @cache_expires_in, cache_tag: @cache_tag}
end

def validate_request!(params)
Expand Down
34 changes: 29 additions & 5 deletions lib/graphiti/resource_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ module Graphiti
class ResourceProxy
include Enumerable

attr_reader :resource, :query, :scope, :payload, :cache_expires_in, :cache
attr_reader :resource, :query, :scope, :payload, :cache_expires_in, :cache, :cache_tag

def initialize(resource, scope, query,
def initialize(
resource,
scope,
query,
payload: nil,
single: false,
raise_on_missing: false,
cache: nil,
cache_expires_in: nil)
cache_expires_in: nil,
cache_tag: nil
)

@resource = resource
@scope = scope
Expand All @@ -19,6 +24,7 @@ def initialize(resource, scope, query,
@raise_on_missing = raise_on_missing
@cache = cache
@cache_expires_in = cache_expires_in
@cache_tag = cache_tag
end

def cache?
Expand Down Expand Up @@ -207,12 +213,30 @@ def etag
"W/#{ActiveSupport::Digest.hexdigest(cache_key_with_version.to_s)}"
end

def resource_cache_tag
return unless @cache_tag.present? && @resource.respond_to?(@cache_tag)

@resource.try(@cache_tag)
end

def cache_key
ActiveSupport::Cache.expand_cache_key([@scope.cache_key, @query.cache_key])
ActiveSupport::Cache.expand_cache_key(
[
@scope.cache_key,
@query.cache_key,
resource_cache_tag
].compact_blank
)
end

def cache_key_with_version
ActiveSupport::Cache.expand_cache_key([@scope.cache_key_with_version, @query.cache_key])
ActiveSupport::Cache.expand_cache_key(
[
@scope.cache_key_with_version,
@query.cache_key,
resource_cache_tag
].compact_blank
)
end

private
Expand Down
13 changes: 10 additions & 3 deletions lib/graphiti/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,29 @@ def jsonapi_render_options

def proxy(base = nil, opts = {})
base ||= jsonapi_resource.base_scope
scope_opts = opts.slice :sideload_parent_length,
scope_opts = opts.slice(
:sideload_parent_length,
:default_paginate,
:after_resolve,
:sideload,
:parent,
:params,
:bypass_required_filters
)

scope = jsonapi_scope(base, scope_opts)
ResourceProxy.new jsonapi_resource,

::Graphiti::ResourceProxy.new(
jsonapi_resource,
scope,
query,
payload: deserialized_payload,
single: opts[:single],
raise_on_missing: opts[:raise_on_missing],
cache: opts[:cache],
cache_expires_in: opts[:cache_expires_in]
cache_expires_in: opts[:cache_expires_in],
cache_tag: opts[:cache_tag]
)
end
end
end
4 changes: 3 additions & 1 deletion lib/graphiti/util/cache_debug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ def last_version
end

def name
"#{Graphiti.context[:object]&.request&.method} #{Graphiti.context[:object]&.request&.url}"
tag = proxy.resource_cache_tag

"#{::Graphiti.context[:object]&.request&.method} #{::Graphiti.context[:object]&.request&.url} #{tag}"
end

def key
Expand Down
11 changes: 9 additions & 2 deletions spec/resource_proxy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,20 @@
let(:query) { double(cache_key: "query-hash") }
let(:scope) { double(cache_key: "scope-hash", cache_key_with_version: "scope-hash-123456") }

subject { described_class.new(resource, scope, query, **{}) }
subject { described_class.new(resource, scope, query, **{cache_tag: :cache_tag}) }

it "cache_key combines query and scope cache keys" do
it "cache_key combines query and scope cache keys if no tags are set" do
cache_key = subject.cache_key
expect(cache_key).to eq("scope-hash/query-hash")
end

it "cache_key combines query, scope and tag cache keys if a tag is set" do
allow(resource).to receive(:cache_tag).and_return("tag_value")

cache_key = subject.cache_key
expect(cache_key).to eq("scope-hash/query-hash/tag_value")
end

it "generates stable etag" do
instance1 = described_class.new(resource, scope, query, **{})
instance2 = described_class.new(resource, scope, query, **{})
Expand Down
Loading