Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load folder data asynchronously. #906

Merged
merged 3 commits into from
Mar 14, 2018
Merged
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
9 changes: 9 additions & 0 deletions app/controllers/ephemera_boxes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ class EphemeraBoxesController < BaseResourceController
before_action :load_collections, only: [:new, :edit]
before_action :cache_project, only: :destroy

def folders
authorize! :read, resource
render json: { data: datatables_folders }
end

def datatables_folders
FolderDataSource.new(resource: resource.decorate, helper: self).data
end

def attach_drive
edit
end
Expand Down
8 changes: 8 additions & 0 deletions app/controllers/ephemera_projects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ def index
render 'index'
end

def folders
render json: { data: datatables_folders }
end

def datatables_folders
FolderDataSource.new(resource: resource.decorate, helper: self).data
end

def manifest
@resource = find_resource(params[:id])
respond_to do |f|
Expand Down
4 changes: 4 additions & 0 deletions app/decorators/ephemera_box_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def folders
@folders ||= members.select { |r| r.is_a?(EphemeraFolder) }.map(&:decorate).to_a
end

def folders_count
@folders_count ||= query_service.custom_queries.count_members(resource: model, model: EphemeraFolder)
end

def ephemera_projects
@ephemera_projects ||= query_service.find_parents(resource: model).map(&:decorate).to_a
end
Expand Down
10 changes: 7 additions & 3 deletions app/decorators/ephemera_project_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ def members
end

def boxes
@boxes ||= members.select { |r| r.is_a?(EphemeraBox) }.map(&:decorate).to_a
@boxes ||= query_service.find_members(resource: model, model: EphemeraBox).map(&:decorate).to_a
end

def fields
@fields ||= members.select { |r| r.is_a?(EphemeraField) }.map(&:decorate).to_a
@fields ||= query_service.find_members(resource: model, model: EphemeraField).map(&:decorate).to_a
end

def folders
@folders ||= members.select { |r| r.is_a?(EphemeraFolder) }.map(&:decorate).to_a
@folders ||= query_service.find_members(resource: model, model: EphemeraFolder).map(&:decorate).to_a
end

def folders_count
@folders_count ||= query_service.custom_queries.count_members(resource: model, model: EphemeraFolder)
end

def templates
Expand Down
22 changes: 22 additions & 0 deletions app/models/folder_data_source.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true
class FolderDataSource
attr_reader :resource, :helper
delegate :folders, to: :resource
def initialize(resource:, helper:)
@resource = resource
@helper = helper
end

def data
@data ||= folders.map do |folder|
{
folder_number: folder.folder_number,
workflow_state: folder.rendered_state,
title: folder.title,
barcode: folder.barcode,
genre: folder.genre.try(:label) || folder.genre,
actions: helper.render_to_string(partial: "catalog/folder_actions", locals: { resource: resource, folder: folder }, formats: [:html])
}
end
end
end
42 changes: 42 additions & 0 deletions app/queries/count_members.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true
class CountMembers
def self.queries
[:count_members]
end

attr_reader :query_service
delegate :orm_class, to: :resource_factory
delegate :resource_factory, to: :query_service
def initialize(query_service:)
@query_service = query_service
end

def count_members(resource:, model: nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tpendragon can you add a comment here to clarify what happens if you do / don't provide a model? It would count all members if you don't, or just members of a certain model if you do?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, yes I see it in the spec. I still think it would be good to have a comment. I can PR it.

if model
orm_class.connection.execute(find_members_with_resource_query(resource, model)).first["count"]
else
orm_class.connection.execute(find_members_query(resource)).first["count"]
end
end

def find_members_query(resource)
<<-SQL
SELECT COUNT(*) FROM orm_resources a,
jsonb_array_elements(a.metadata->'member_ids') AS b(member)
JOIN orm_resources member ON (b.member->>'id')::#{id_type} = member.id WHERE a.id = '#{resource.id}'
SQL
end

def find_members_with_resource_query(resource, model)
<<-SQL
SELECT COUNT(*) FROM orm_resources a,
jsonb_array_elements(a.metadata->'member_ids') AS b(member)
JOIN orm_resources member ON (b.member->>'id')::#{id_type} = member.id WHERE a.id = '#{resource.id}'
AND member.internal_resource = '#{model}'
SQL
end

def id_type
@id_type ||= orm_class.columns_hash["id"].type
end
end
5 changes: 5 additions & 0 deletions app/views/catalog/_folder_actions.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<td>
<% folder_url = parent_solr_document_path("#{resource.id}", "#{folder.id}") %>
<%= link_to 'View', folder_url, class: 'btn btn-default' %>
<%= link_to 'Edit', main_app.polymorphic_path([:edit, folder]), class: 'btn btn-default' %>
</td>
33 changes: 8 additions & 25 deletions app/views/catalog/_members_ephemera_box.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,21 @@
<%# DataTable looks for certain data attributes on initialization
# https://datatables.net/examples/advanced_init/html5-data-options.html
# here we sort descending by folder number %>
<table id="members" class="table table-striped datatable" data-order='[[0, "desc"]]'>
<table id="members" class="table table-striped datatable" data-ajax='<%= polymorphic_path([:folders, resource], format: :json) %>' data-order='[[0, "desc"]]'>
<thead>
<tr>
<% if decorated_resource.is_a?(EphemeraBox) %>
<th>Folder Number</th>
<th data-data="folder_number">Folder Number</th>
<% end %>
<th>Workflow State</th>
<th>Title</th>
<th>Barcode</th>
<th>Genre</th>
<th>Actions</th>
<th data-data="workflow_state">Workflow State</th>
<th data-data="title">Title</th>
<th data-data="barcode">Barcode</th>
<th data-data="genre">Genre</th>
<th data-data="actions">Actions</th>
</tr>
</thead>
<tbody>
<% unless decorated_resource.folders.empty? %>
<% decorated_resource.folders.each do |folder| %>
<% folder_url = parent_solr_document_path("#{resource.id}", "#{folder.id}") %>
<tr>
<% if decorated_resource.is_a?(EphemeraBox) %>
<td class="folder_number"><%= link_to folder.folder_number, folder_url %></td>
<% end %>
<td><%= folder.rendered_state %></td>
<td><%= folder.header %></td>
<td class="barcode"><%= folder.barcode %></td>
<td class="genre"><%= folder.genre %></td>
<td>
<%= link_to 'View', folder_url, class: 'btn btn-default' %>
<%= link_to 'Edit', main_app.polymorphic_path([:edit, folder]), class: 'btn btn-default' %>
</td>
</tr>
<% end %>
<% else %>
<% if decorated_resource.folders_count == 0 %>
<td>This work has no folders attached. Click "Attach Folder" to attach folders.</td>
<% end %>
</tbody>
Expand Down
3 changes: 2 additions & 1 deletion config/initializers/valkyrie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@
FindProjectFolders,
FindIdentifiersToReconcile,
FileSetsSortedByUpdated,
FindFixityFailures
FindFixityFailures,
CountMembers
].each do |query_handler|
Valkyrie.config.metadata_adapter.query_service.custom_queries.register_query_handler(query_handler)
end
Expand Down
6 changes: 6 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@
resources :ephemera_projects do
resources :templates, only: [:new, :create, :destroy]
resources :ephemera_fields
member do
get :folders, defaults: { format: :json }
end
member do
get :manifest, defaults: { format: :json }
end
Expand All @@ -106,6 +109,9 @@
member do
get :attach_drive
end
member do
get :folders, defaults: { format: :json }
end
end

resources :ephemera_folders do
Expand Down
32 changes: 32 additions & 0 deletions spec/controllers/ephemera_boxes_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,38 @@
end
end

describe "folders" do
context "when not logged in" do
let(:user) { nil }
it "redirects CanCan::AccessDenied error to login" do
box = FactoryBot.create_for_repository(:private_ephemera_box)
get :folders, params: { id: box.id }
expect(response).to redirect_to('/users/auth/cas')
end
end
context "when they have permission" do
let(:user) { FactoryBot.create(:admin) }
render_views
it "renders a JSON list of a project's folders" do
folder = FactoryBot.create_for_repository(:ephemera_folder)
box = FactoryBot.create_for_repository(:ephemera_box, member_ids: folder.id)

get :folders, params: { id: box.id.to_s, formats: :json }

json = JSON.parse(response.body)
expect(json["data"].length).to eq 1
expect(json["data"][0]["folder_number"]).to eq folder.folder_number.first
expect(json["data"][0]["workflow_state"]).to eq "<span class=\"label label-info\">Needs QA</span>"
expect(json["data"][0]["title"]).to eq folder.title
expect(json["data"][0]["barcode"]).to eq folder.barcode.first
expect(json["data"][0]["genre"]).to eq folder.genre.first
expect(json["data"][0]["actions"]).to have_link "View", href: "/catalog/parent/#{box.id}/#{folder.id}"
expect(json["data"][0]["actions"]).to have_link "Edit", href: "/concern/ephemera_folders/#{folder.id}/edit"
expect(json["data"][0]["actions"]).not_to have_link "Delete"
end
end
end

describe "create" do
let(:user) { FactoryBot.create(:admin) }
let(:valid_params) do
Expand Down
22 changes: 22 additions & 0 deletions spec/controllers/ephemera_projects_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,28 @@
end
end

describe "folders" do
context "when they have permission" do
let(:user) { FactoryBot.create(:admin) }
render_views
it "renders a JSON list of a project's folders" do
folder = FactoryBot.create_for_repository(:ephemera_folder)
project = FactoryBot.create_for_repository(:ephemera_project, member_ids: folder.id)

get :folders, params: { id: project.id.to_s, formats: :json }

json = JSON.parse(response.body)
expect(json["data"].length).to eq 1
expect(json["data"][0]["folder_number"]).to eq folder.folder_number.first
expect(json["data"][0]["workflow_state"]).to eq "<span class=\"label label-info\">Needs QA</span>"
expect(json["data"][0]["title"]).to eq folder.title
expect(json["data"][0]["barcode"]).to eq folder.barcode.first
expect(json["data"][0]["genre"]).to eq folder.genre.first
expect(json["data"][0]["actions"]).not_to be_blank
end
end
end

describe "destroy" do
let(:user) { FactoryBot.create(:admin) }
context "access control" do
Expand Down
19 changes: 19 additions & 0 deletions spec/queries/count_members_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'rails_helper'

RSpec.describe CountMembers do
subject(:query) { described_class.new(query_service: query_service) }
let(:box) { FactoryBot.create_for_repository(:ephemera_box, member_ids: folder.id) }
let(:folder) { FactoryBot.create_for_repository(:ephemera_folder) }
let(:query_service) { Valkyrie.config.metadata_adapter.query_service }

describe "#count_members" do
it "can give a count of all members" do
expect(query.count_members(resource: box)).to eq 1
end
it "can give a count of all members of a specific type" do
expect(query.count_members(resource: box, model: EphemeraFolder)).to eq 1
expect(query.count_members(resource: box, model: ScannedResource)).to eq 0
end
end
end
16 changes: 2 additions & 14 deletions spec/views/catalog/_members_ephemera_box.html.erb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,7 @@
it 'shows them' do
expect(rendered).to have_selector 'h2', text: 'Folders'
expect(rendered).to have_selector 'th', text: 'Folder Number'
expect(rendered).to have_selector 'td.folder_number', text: "one"
expect(rendered).to have_link "one", href: "/catalog/parent/#{parent.id}/#{child.id}"
expect(rendered).not_to have_selector 'td.folder_number', text: "[\"one\"]"
expect(rendered).to have_selector 'td.barcode', text: child.barcode.first
expect(rendered).not_to have_selector 'td.barcode', text: "[\"#{child.barcode.first}\"]"
expect(rendered).to have_selector 'td.genre', text: child.genre.first
expect(rendered).not_to have_selector 'td.genre', text: "[\"#{child.genre.first}\"]"
expect(rendered).to have_link "View", href: "/catalog/parent/#{parent.id}/#{child.id}"
expect(rendered).to have_link "Edit", href: "/concern/ephemera_folders/#{child.id}/edit"
expect(rendered).not_to have_link "Delete", href: "/concern/ephemera_folders/#{child.id}"
expect(rendered).to have_selector "table.datatable[data-ajax='/concern/ephemera_boxes/#{parent.id}/folders']"
end
end
context "when it's a project with folders" do
Expand All @@ -40,10 +31,7 @@
it 'shows them' do
expect(rendered).to have_selector 'h2', text: 'Folders'
expect(rendered).not_to have_selector 'th', text: 'Folder Number'
expect(rendered).to have_selector 'td', text: 'test folder'
expect(rendered).to have_link "View", href: "/catalog/parent/#{parent.id}/#{child.id}"
expect(rendered).to have_link "Edit", href: "/concern/ephemera_folders/#{child.id}/edit"
expect(rendered).not_to have_link "Delete", href: "/concern/ephemera_folders/#{child.id}"
expect(rendered).to have_selector "table.datatable[data-ajax='/concern/ephemera_projects/#{parent.id}/folders']"
end
end
end
5 changes: 0 additions & 5 deletions spec/views/catalog/_members_ephemera_project.html.erb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@
expect(rendered).to have_link 'Edit', href: edit_ephemera_box_path(child.id)

expect(rendered).to have_selector 'h2', text: 'Folders'
expect(rendered).to have_selector 'span.label-info', text: 'Needs QA'
expect(rendered).to have_selector 'td', text: 'test folder'
expect(rendered).to have_selector 'td', text: '12345678901234'
expect(rendered).to have_link 'View', href: parent_solr_document_path(parent.id, child_folder.id)
expect(rendered).to have_link 'Edit', href: edit_ephemera_folder_path(child_folder.id)
end
end
context "when it's a project with templates" do
Expand Down