Skip to content

Commit

Permalink
Downloading and updating file content
Browse files Browse the repository at this point in the history
  • Loading branch information
escowles committed Aug 11, 2017
1 parent 876b00c commit 64270c1
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 0 deletions.
9 changes: 9 additions & 0 deletions app/decorators/upload_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true
class UploadDecorator < Draper::Decorator
attr_reader :original_filename
delegate_all
def initialize(file, original_filename)
super(file)
@original_filename = original_filename
end
end
12 changes: 12 additions & 0 deletions app/services/file_appender.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ def initialize(storage_adapter:, persister:, files:)

def append_to(resource)
return [] if files.blank?
updated_files = update_files(resource, files)
return updated_files unless updated_files.empty?

file_sets = build_file_sets || file_nodes
if resource.respond_to?(:file_metadata)
resource.file_metadata += file_sets
Expand All @@ -20,6 +23,15 @@ def append_to(resource)
file_sets
end

def update_files(resource, files)
files.select { |file| file.is_a?(Hash) }.map do |file|
node = resource.file_metadata.select { |x| x.id.to_s == file.keys.first }.first
file_wrapper = UploadDecorator.new(file.values.first, node.original_filename.first)
file = storage_adapter.upload(file: file_wrapper, resource: node)
node
end
end

def build_file_sets
return if processing_derivatives?
file_nodes.each_with_index.map do |node, index|
Expand Down
24 changes: 24 additions & 0 deletions app/views/catalog/_admin_controls.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
<% if resource.kind_of?(FileSet) %>
<h2>Files</h2>
<%= simple_form_for FileSetChangeSet.new(resource) do |f| %>
<table class="table table-striped files">
<tr>
<th>Label</th>
<th>Download</th>
<th>Update</th>
</tr>
<% resource.file_metadata.each do |file| %>
<tr>
<td><%= file.label.first %></td>
<td><%= link_to "Download", valhalla.download_path(resource.id, file.id) %></td>
<td><%= f.input "files[][#{file.id}]", as: :file, label: false, required: false %></td>
</tr>
<% end %>
<tr>
<td colspan="2"></td>
<td><%= f.submit 'Update Files', class: 'btn btn-primary' %></td>
</tr>
</table>
<% end %>
<% end %>

<div class="form-actions">
<%= link_to "Edit This #{resource.human_readable_type}", main_app.polymorphic_path([:edit, resource]), class: 'btn btn-default' %>
<% if decorated_resource.manageable_files? %>
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
get "/iiif/lookup/:prefix/:naan/:arkid", to: 'catalog#lookup_manifest', as: :lookup_manifest

mount BrowseEverything::Engine => '/browse'
mount Valhalla::Engine => '/'

if Rails.env.development? || Rails.env.test?
mount Riiif::Engine => '/image-service', as: 'riiif'
Expand Down
24 changes: 24 additions & 0 deletions spec/change_set_persisters/plum_change_set_persister_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,30 @@
expect(query_service.find_all.to_a.map(&:class)).to contain_exactly ScannedResource, FileSet
end
end
describe "updating files" do
let(:file1) { fixture_file_upload('files/example.tif', 'image/tiff') }
let(:file2) { fixture_file_upload('files/holding_locations.json', 'application/json') }
it "can append files as FileSets", run_real_derivatives: true do
# upload a file
resource = FactoryGirl.build(:scanned_resource)
change_set = change_set_class.new(resource)
change_set.files = [file1]
output = change_set_persister.save(change_set: change_set)
file_set = query_service.find_members(resource: output).first
file_node = file_set.file_metadata.find { |x| x.use == [Valkyrie::Vocab::PCDMUse.OriginalFile] }
file = storage_adapter.find_by(id: file_node.file_identifiers.first)
expect(file.size).to eq 196_882

# update the file
change_set = FileSetChangeSet.new(file_set)
change_set.files = [{ file_node.id.to_s => file2 }]
change_set_persister.save(change_set: change_set)
updated_file_set = query_service.find_by(id: file_set.id)
updated_file_node = updated_file_set.file_metadata.find { |x| x.id == file_node.id }
updated_file = storage_adapter.find_by(id: updated_file_node.file_identifiers.first)
expect(updated_file.size).to eq 5600
end
end

describe "collection interactions" do
context "when a collection is deleted" do
Expand Down
48 changes: 48 additions & 0 deletions spec/controllers/valhalla/downloads_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'rails_helper'

RSpec.describe Valhalla::DownloadsController do
let(:meta) { Valkyrie.config.metadata_adapter }
let(:disk) { Valkyrie.config.storage_adapter }
let(:change_set_persister) { PlumChangeSetPersister.new(metadata_adapter: meta, storage_adapter: disk) }
let(:sample_file) { fixture_file_upload('files/example.tif', 'image/tiff') }
let(:resource) { FactoryGirl.create_for_repository(:scanned_resource, files: [sample_file]) }
let(:file_set) { resource.member_ids.map { |id| meta.query_service.find_by(id: id) }.first }
let(:file_node) { file_set.file_metadata.first }
let(:user) { FactoryGirl.create(:admin) }

routes { Valhalla::Engine.routes }

describe 'GET /downloads/:obj/file/:id' do
context "when logged in" do
before do
sign_in user if user
end

it "serves files that exist" do
get :show, params: { resource_id: file_set.id.to_s, id: file_node.id.to_s }
expect(response.body).to eq(sample_file.read)
expect(response.content_length).to eq(196_882)
expect(response.content_type).to eq('image/tiff')
expect(response.headers['Content-Disposition']).to eq('inline; filename="example.tif"')
end

it "returns an appropriate error when the file doesn't exist" do
get :show, params: { resource_id: file_set.id.to_s, id: 'bogus' }
expect(response.status).to eq(404)
end

it "returns an appropriate error when the resource doesn't exist" do
get :show, params: { resource_id: 'bogus', id: 'bogus' }
expect(response.status).to eq(404)
end
end

context "when not logged in" do
it "redirects to login" do
get :show, params: { resource_id: file_set.id.to_s, id: file_node.id.to_s }
expect(response).to redirect_to('/users/auth/cas')
end
end
end
end
70 changes: 70 additions & 0 deletions valhalla/app/controllers/valhalla/downloads_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true
class Valhalla::DownloadsController < ApplicationController
include Hydra::Controller::DownloadBehavior

def show
if resource && load_file
send_content
else
render_404
end
end

def resource
@resource ||= query_service.find_by(id: params[:resource_id])
rescue Valkyrie::Persistence::ObjectNotFoundError
nil
end

def load_file
return unless binary_file && file_desc
@load_file ||= FileWithMetadata.new(id: params[:id], file: binary_file, mime_type: file_desc.mime_type, original_name: file_desc.original_filename.first)
end

def file_desc
return unless resource
@file_desc ||= resource.file_metadata.find { |m| m.id.to_s == params[:id] }
end

def binary_file
return unless file_desc
@binary_file ||= storage_adapter.find_by(id: file_desc.file_identifiers.first)
end

class FileWithMetadata < Dry::Struct
delegate :size, :read, :stream, to: :file
attribute :id, Valkyrie::Types::Any
attribute :file, Valkyrie::Types::Any
attribute :mime_type, Valkyrie::Types::SingleValuedString
attribute :original_name, Valkyrie::Types::SingleValuedString
end

# Customize the :download ability in your Ability class, or override this method
def authorize_download!
authorize! :download, resource
end

# Copied from hydra-head and adjusted to handle the fact that we don't have a
# modified_date in Valkyrie yet.
def prepare_file_headers
send_file_headers! content_options
response.headers['Content-Type'] = file_desc.mime_type.first.to_s
response.headers['Content-Length'] ||= binary_file.size.to_s
# Prevent Rack::ETag from calculating a digest over body
response.headers['Last-Modified'] = modified_date
end

def modified_date
return unless load_file.respond_to?(:updated_at)
# Copied/pasted from Hydra-Head.
file_desc.updated_at.utc.strftime("%a, %d %b %Y %T GMT")
end

def query_service
Valkyrie.config.metadata_adapter.query_service
end

def storage_adapter
Valkyrie.config.storage_adapter
end
end
1 change: 1 addition & 0 deletions valhalla/config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# frozen_string_literal: true
Valhalla::Engine.routes.draw do
get '/downloads/:resource_id/file/:id', to: 'downloads#show', as: :download
end

0 comments on commit 64270c1

Please sign in to comment.