Skip to content

Commit

Permalink
Merge pull request #1266 from dradis/liquid/cached-assigns
Browse files Browse the repository at this point in the history
Implement LiquidCachedAssigns
  • Loading branch information
aapomm committed Jun 27, 2024
2 parents bbb8543 + 604b95b commit b027497
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 121 deletions.
2 changes: 1 addition & 1 deletion app/controllers/concerns/liquid_enabled_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ def project_assigns
project = Project.find(params[:project_id])
authorize! :use, project

LiquidAssignsService.new(project: project, text: params[:text]).assigns
LiquidCachedAssigns.new(project: project)
end
end
61 changes: 0 additions & 61 deletions app/services/liquid_assigns_service.rb

This file was deleted.

70 changes: 70 additions & 0 deletions app/services/liquid_cached_assigns.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
class LiquidCachedAssigns < Hash
AVAILABLE_PROJECT_ASSIGNS = %w{ evidences issues nodes notes project tags }.freeze

attr_accessor :assigns, :project

def initialize(project:)
@project = project

@assigns = { 'project' => ProjectDrop.new(project) }
@assigns.merge!(assigns_pro)
end

def [](record_type)
assigns[record_type] ||= cached_drops(record_type)
end

# SEE: https://github.com/Shopify/liquid/blob/77bc56/lib/liquid/context.rb#L211
# Liquid is checking if the variable is present in the assigns hash by
# calling the `key?` method. Since we're lazily loading the keys, the variable
# may not yet be present in the assigns hash.
def key?(key)
AVAILABLE_PROJECT_ASSIGNS.include?(key.to_s) || assigns.key?(key)
end

def merge(hash)
lca = LiquidCachedAssigns.new(project: project)
lca.assigns = @assigns.merge(hash)
lca
end

def merge!(hash)
@assigns.merge!(hash)
self
end

private

def assigns_pro
{}
end

def cached_drops(record_type)
records = project_records(record_type)

return [] if records.empty?

cache_key = ActiveSupport::Cache.expand_cache_key([project.id, records], 'liquid')
drop_class = "#{record_type.singularize.camelize}Drop".constantize

Rails.cache.fetch(cache_key) do
records.map { |record| drop_class.new(record) }
end
end

def project_records(record_type)
return [] unless AVAILABLE_PROJECT_ASSIGNS.include?(record_type)

case record_type
when 'evidences'
project.evidence
when 'nodes'
project.nodes.user_nodes
when 'notes'
# FIXME - ISSUE/NOTE INHERITANCE
project.notes.where.not(node_id: project.issue_library.id)
else
project.send(record_type.to_sym)
end
end
end
59 changes: 0 additions & 59 deletions spec/services/liquid_assigns_service_spec.rb

This file was deleted.

63 changes: 63 additions & 0 deletions spec/services/liquid_cached_assigns_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require 'rails_helper'

RSpec.describe LiquidCachedAssigns do
let!(:project) { create(:project) }
let(:liquid_assigns) { described_class.new(project: project) }

before do
node = create(:node, project: project)
issue = create(:issue, node: project.issue_library)
create(:evidence, issue: issue, node: node)
create(:note, node: node)
create(:tag)
end

context 'fetching an assign from an available collection' do
it 'lazily loads the assigns' do
expect(liquid_assigns.assigns.keys).to_not include(
%w{issues evidences nodes notes tags}
)
end

it 'builds a hash of liquid assigns' do
issues = project.issues.map(&:title)

expect(liquid_assigns['project'].name).to eq(project.name)
expect(liquid_assigns['issues'].map(&:title)).to eq(issues)
expect(liquid_assigns['evidences'].map(&:title)).to eq(project.evidence.map(&:title))
expect(liquid_assigns['nodes'].map(&:label)).to eq(project.nodes.user_nodes.map(&:label))
expect(liquid_assigns['notes'].map(&:title)).to eq(project.notes.map(&:title) - issues)
expect(liquid_assigns['tags'].map(&:display_name)).to eq(project.tags.map(&:display_name))
end
end

context 'fetching an assign from a unavailable collection' do
it 'returns an empty array' do
expect(liquid_assigns['fake']).to be_empty
end
end

context 'with pro records', skip: !defined?(Dradis::Pro) do
let!(:project) { create(:project, :with_team) }

before do
report_content = project.content_library
report_content.properties = { 'dradis.project' => project.name }
report_content.save

create(:content_block, project: project)
end

context 'fetching an assign from an available collection' do
it 'lazily loads the assigns' do
expect(liquid_assigns.assigns.keys).to_not include('content_blocks')
end

it 'builds a hash with Dradis::Pro assigns' do
expect(liquid_assigns['document_properties'].available_properties).to eq({ 'dradis.project' => project.name })
expect(liquid_assigns['team'].name).to eq(project.team.name)
expect(liquid_assigns['content_blocks'].map(&:content)).to eq(project.content_blocks.map(&:content))
end
end
end
end

0 comments on commit b027497

Please sign in to comment.