ddr-models 2.3.2 → 2.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -3
  3. data/ddr-models.gemspec +1 -1
  4. data/lib/ddr/auth.rb +10 -6
  5. data/lib/ddr/auth/group.rb +2 -1
  6. data/lib/ddr/auth/grouper_gateway.rb +7 -6
  7. data/lib/ddr/auth/permissions.rb +2 -1
  8. data/lib/ddr/auth/roles.rb +3 -2
  9. data/lib/ddr/auth/roles/role_set.rb +3 -2
  10. data/lib/ddr/datastreams/administrative_metadata_datastream.rb +3 -0
  11. data/lib/ddr/derivatives/png_generator.rb +1 -1
  12. data/lib/ddr/index/fields.rb +11 -1
  13. data/lib/ddr/index/filter.rb +4 -3
  14. data/lib/ddr/index/legacy_license_fields.rb +0 -2
  15. data/lib/ddr/index/query_clause.rb +2 -2
  16. data/lib/ddr/index/query_value.rb +6 -3
  17. data/lib/ddr/jobs.rb +5 -1
  18. data/lib/ddr/jobs/fits_file_characterization.rb +2 -40
  19. data/lib/ddr/jobs/fixity_check.rb +13 -0
  20. data/lib/ddr/jobs/job.rb +36 -0
  21. data/lib/ddr/jobs/queue.rb +27 -0
  22. data/lib/ddr/jobs/update_index.rb +13 -0
  23. data/lib/ddr/managers/derivatives_manager.rb +21 -17
  24. data/lib/ddr/models.rb +14 -2
  25. data/lib/ddr/models/access_controllable.rb +3 -6
  26. data/lib/ddr/models/admin_set.rb +5 -3
  27. data/lib/ddr/models/base.rb +2 -3
  28. data/lib/ddr/models/contact.rb +17 -0
  29. data/lib/ddr/models/file_characterization.rb +37 -0
  30. data/lib/ddr/models/has_admin_metadata.rb +2 -1
  31. data/lib/ddr/models/has_content.rb +4 -2
  32. data/lib/ddr/models/indexing.rb +5 -0
  33. data/lib/ddr/models/licenses/license.rb +5 -3
  34. data/lib/ddr/models/solr_document.rb +1 -1
  35. data/lib/ddr/models/version.rb +1 -1
  36. data/lib/ddr/models/with_content_file.rb +37 -0
  37. data/lib/ddr/vocab/asset.rb +3 -0
  38. data/spec/derivatives/png_generator_spec.rb +7 -0
  39. data/spec/factories/test_model_factories.rb +1 -1
  40. data/spec/fixtures/arrow1rightred_e0.gif +0 -0
  41. data/spec/fixtures/bird.jpg +0 -0
  42. data/spec/index/query_value_spec.rb +33 -0
  43. data/spec/jobs/fits_file_characterization_spec.rb +8 -44
  44. data/spec/jobs/fixity_check_spec.rb +22 -0
  45. data/spec/jobs/job_spec.rb +40 -0
  46. data/spec/jobs/update_index_spec.rb +22 -0
  47. data/spec/managers/derivatives_manager_spec.rb +16 -12
  48. data/spec/models/admin_set_spec.rb +3 -3
  49. data/spec/models/contact_spec.rb +22 -0
  50. data/spec/models/effective_license_spec.rb +17 -7
  51. data/spec/models/file_characterization_spec.rb +39 -0
  52. data/spec/models/has_admin_metadata_spec.rb +8 -5
  53. data/spec/models/indexing_spec.rb +2 -0
  54. data/spec/models/license_spec.rb +3 -1
  55. data/spec/models/solr_document_spec.rb +6 -3
  56. data/spec/models/with_content_file_spec.rb +37 -0
  57. data/spec/spec_helper.rb +2 -0
  58. metadata +43 -30
  59. data/lib/ddr/contacts.rb +0 -25
  60. data/lib/ddr/index_fields.rb +0 -14
  61. data/spec/auth/ldap_gateway_spec.rb +0 -9
  62. data/spec/contacts/contacts_spec.rb +0 -26
@@ -51,23 +51,27 @@ module Ddr
51
51
  end
52
52
 
53
53
  def generate_derivative!(derivative)
54
- tempdir = FileUtils.mkdir(File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname('',nil))).first
55
- generator_source = create_source_file(tempdir)
56
- generator_output = File.new(File.join(tempdir, "output.out"), 'wb')
57
- results = derivative.generator.new(generator_source.path, generator_output.path, derivative.options).generate
58
- generator_source.close unless generator_source.closed?
59
- if results.status.success?
60
- generator_output = File.open(generator_output, 'rb')
61
- object.reload if object.persisted?
62
- object.add_file generator_output, derivative.datastream, mime_type: derivative.generator.output_mime_type
63
- object.save!
64
- else
65
- Rails.logger.error results.stderr
66
- raise Ddr::Models::DerivativeGenerationFailure,
67
- "Failure generating #{derivative.name} for #{object.pid}: #{results.stderr}"
54
+ tempdir_path = File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname('',nil))
55
+ begin
56
+ tempdir = FileUtils.mkdir(tempdir_path).first
57
+ generator_source = create_source_file(tempdir)
58
+ generator_output = File.new(File.join(tempdir, "output.out"), 'wb')
59
+ results = derivative.generator.new(generator_source.path, generator_output.path, derivative.options).generate
60
+ generator_source.close unless generator_source.closed?
61
+ if results.status.success?
62
+ generator_output = File.open(generator_output, 'rb')
63
+ object.reload if object.persisted?
64
+ object.add_file generator_output, derivative.datastream, mime_type: derivative.generator.output_mime_type
65
+ object.save!
66
+ else
67
+ Rails.logger.error results.stderr
68
+ raise Ddr::Models::DerivativeGenerationFailure,
69
+ "Failure generating #{derivative.name} for #{object.pid}: #{results.stderr}"
70
+ end
71
+ generator_output.close unless generator_output.closed?
72
+ ensure
73
+ FileUtils.remove_dir(tempdir_path) if File.exist?(tempdir_path)
68
74
  end
69
- generator_output.close unless generator_output.closed?
70
- FileUtils.remove_dir(tempdir)
71
75
  end
72
76
 
73
77
  def delete_derivative(derivative)
@@ -110,7 +114,7 @@ module Ddr
110
114
  when :multires_image
111
115
  object.content_type == "image/tiff"
112
116
  when :thumbnail
113
- object.image? || object.pdf?
117
+ object.image?
114
118
  else
115
119
  false
116
120
  end
@@ -11,15 +11,14 @@ require 'hydra/validations'
11
11
 
12
12
  module Ddr
13
13
  extend ActiveSupport::Autoload
14
+ extend Deprecation
14
15
 
15
16
  autoload :Actions
16
17
  autoload :Auth
17
- autoload :Contacts
18
18
  autoload :Datastreams
19
19
  autoload :Derivatives
20
20
  autoload :Events
21
21
  autoload :Index
22
- autoload :IndexFields
23
22
  autoload :Jobs
24
23
  autoload :Managers
25
24
  autoload :Metadata
@@ -27,6 +26,16 @@ module Ddr
27
26
  autoload :Utils
28
27
  autoload :Vocab
29
28
 
29
+ def self.const_missing(name)
30
+ if name == :IndexFields
31
+ Deprecation.warn(Ddr::Models, "`Ddr::IndexFields` is deprecated and will be removed in ddr-models 3.0." \
32
+ " Use `Ddr::Index::Fields` instead.")
33
+ Index::Fields
34
+ else
35
+ super
36
+ end
37
+ end
38
+
30
39
  module Models
31
40
  extend ActiveSupport::Autoload
32
41
 
@@ -34,11 +43,13 @@ module Ddr
34
43
  autoload :AdminSet
35
44
  autoload :Base
36
45
  autoload :ChecksumInvalid, 'ddr/models/error'
46
+ autoload :Contact
37
47
  autoload :ContentModelError, 'ddr/models/error'
38
48
  autoload :DerivativeGenerationFailure, 'ddr/models/error'
39
49
  autoload :Describable
40
50
  autoload :Error
41
51
  autoload :EventLoggable
52
+ autoload :FileCharacterization
42
53
  autoload :FileManagement
43
54
  autoload :FindingAid
44
55
  autoload :FixityCheckable
@@ -54,6 +65,7 @@ module Ddr
54
65
  autoload :SolrDocument
55
66
  autoload :StructDiv
56
67
  autoload :Structure
68
+ autoload :WithContentFile
57
69
  autoload :YearFacet
58
70
 
59
71
  autoload_under "licenses" do
@@ -2,26 +2,23 @@ module Ddr
2
2
  module Models
3
3
  module AccessControllable
4
4
  extend ActiveSupport::Concern
5
+ extend Deprecation
5
6
 
6
7
  included do
7
8
  include Hydra::AccessControls::Permissions
8
9
  end
9
10
 
10
11
  def set_initial_permissions(user_creator = nil)
11
- warn "[DEPRECATION] `set_initial_permissions` is deprecated" \
12
- " and should not be used with role-based access control" \
13
- " (#{caller.first})."
14
12
  if user_creator
15
13
  self.permissions_attributes = [{type: "user", access: "edit", name: user_creator.to_s}]
16
14
  end
17
15
  end
16
+ deprecation_deprecate :set_initial_permissions
18
17
 
19
18
  def copy_permissions_from(other)
20
- warn "[DEPRECATION] `copy_permissions_from` is deprecated" \
21
- " and should not be used with role-based access control" \
22
- " (#{caller.first})."
23
19
  self.permissions_attributes = other.permissions.collect { |p| p.to_hash }
24
20
  end
21
+ deprecation_deprecate :copy_permissions_from
25
22
  end
26
23
  end
27
24
  end
@@ -1,11 +1,13 @@
1
- require "ddr_aux/client"
1
+ require "active_resource"
2
2
 
3
3
  module Ddr::Models
4
- class AdminSet < DdrAux::Client::AdminSet
4
+ class AdminSet < ActiveResource::Base
5
+
6
+ self.site = ENV["DDR_AUX_API_URL"]
5
7
 
6
8
  def self.call(obj)
7
9
  if obj.admin_set
8
- find(code: obj.admin_set)
10
+ get(:find, code: obj.admin_set)
9
11
  end
10
12
  end
11
13
 
@@ -22,9 +22,8 @@ module Ddr
22
22
  end
23
23
 
24
24
  def copy_admin_policy_or_permissions_from(other)
25
- warn "[DEPRECATION] `copy_admin_policy_or_permissions_from` is deprecated." \
26
- " Use `copy_admin_policy_or_roles_from` instead." \
27
- " (#{caller.first})."
25
+ Deprecation.warn(self.class, "`copy_admin_policy_or_permissions_from` is deprecated." \
26
+ " Use `copy_admin_policy_or_roles_from` instead.")
28
27
  copy_admin_policy_or_roles_from(other)
29
28
  end
30
29
 
@@ -0,0 +1,17 @@
1
+ require "active_resource"
2
+
3
+ module Ddr::Models
4
+ class Contact < ActiveResource::Base
5
+
6
+ self.site = ENV["DDR_AUX_API_URL"]
7
+
8
+ def self.call(slug)
9
+ get(:find, slug: slug)
10
+ end
11
+
12
+ def to_s
13
+ name
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ require "delegate"
2
+ require "shellwords"
3
+
4
+ module Ddr::Models
5
+ class FileCharacterization < SimpleDelegator
6
+
7
+ class FITSError < Error; end
8
+
9
+ def self.call(obj)
10
+ new(obj).call
11
+ end
12
+
13
+ def call
14
+ with_content_file do |path|
15
+ fits_output = run_fits(path)
16
+ reload
17
+ fits.content = fits_output
18
+ save!
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def run_fits(path)
25
+ output = `#{fits_command} -i #{Shellwords.escape(path)}`
26
+ unless $?.success?
27
+ raise FITSError, output
28
+ end
29
+ output
30
+ end
31
+
32
+ def fits_command
33
+ File.join(Ddr::Models.fits_home, 'fits.sh')
34
+ end
35
+
36
+ end
37
+ end
@@ -20,6 +20,7 @@ module Ddr::Models
20
20
  :research_help_contact,
21
21
  :workflow_state,
22
22
  :ead_id,
23
+ :aspace_id,
23
24
  datastream: "adminMetadata",
24
25
  multiple: false
25
26
 
@@ -64,7 +65,7 @@ module Ddr::Models
64
65
  end
65
66
 
66
67
  def research_help
67
- Ddr::Contacts.get(research_help_contact) if research_help_contact
68
+ Ddr::Models::Contact.call(research_help_contact) if research_help_contact
68
69
  end
69
70
 
70
71
  def effective_license
@@ -1,5 +1,3 @@
1
- require 'openssl'
2
-
3
1
  module Ddr
4
2
  module Models
5
3
  module HasContent
@@ -128,6 +126,10 @@ module Ddr
128
126
  extractedText.has_content?
129
127
  end
130
128
 
129
+ def with_content_file(&block)
130
+ WithContentFile.new(self, &block)
131
+ end
132
+
131
133
  protected
132
134
 
133
135
  def update_derivatives
@@ -4,6 +4,10 @@ module Ddr
4
4
 
5
5
  include Ddr::Index::Fields
6
6
 
7
+ def self.const_missing(name)
8
+ Ddr::Index::Fields.const_missing(name)
9
+ end
10
+
7
11
  def to_solr(solr_doc=Hash.new, opts={})
8
12
  solr_doc = super(solr_doc, opts)
9
13
  solr_doc.merge index_fields
@@ -13,6 +17,7 @@ module Ddr
13
17
  fields = {
14
18
  ACCESS_ROLE => roles.to_json,
15
19
  ADMIN_SET => admin_set,
20
+ ASPACE_ID => aspace_id,
16
21
  BOX_NUMBER_FACET => desc_metadata_values('box_number'),
17
22
  CREATOR_FACET => creator,
18
23
  DATE_FACET => date,
@@ -1,13 +1,15 @@
1
- require "ddr_aux/client"
1
+ require "active_resource"
2
2
 
3
3
  module Ddr::Models
4
- class License < DdrAux::Client::License
4
+ class License < ActiveResource::Base
5
+
6
+ self.site = ENV["DDR_AUX_API_URL"]
5
7
 
6
8
  attr_accessor :pid
7
9
 
8
10
  def self.call(obj)
9
11
  if obj.license
10
- license = find(url: obj.license)
12
+ license = get(:find, url: obj.license)
11
13
  license.pid = obj.pid
12
14
  license
13
15
  end
@@ -189,7 +189,7 @@ module Ddr::Models
189
189
 
190
190
  def research_help
191
191
  research_help_contact = self[Ddr::Index::Fields::RESEARCH_HELP_CONTACT] || inherited_research_help_contact
192
- Ddr::Contacts.get(research_help_contact) if research_help_contact
192
+ Ddr::Models::Contact.call(research_help_contact) if research_help_contact
193
193
  end
194
194
 
195
195
  def parent_uri
@@ -1,5 +1,5 @@
1
1
  module Ddr
2
2
  module Models
3
- VERSION = "2.3.2"
3
+ VERSION = "2.4.0.rc1"
4
4
  end
5
5
  end
@@ -0,0 +1,37 @@
1
+ require "tempfile"
2
+ require "delegate"
3
+
4
+ module Ddr::Models
5
+ class WithContentFile < SimpleDelegator
6
+
7
+ def initialize(obj, &block)
8
+ super(obj)
9
+ with_temp_file &block
10
+ end
11
+
12
+ # Yields path of tempfile containing content to block
13
+ # @yield [String] the path to the tempfile containing content
14
+ def with_temp_file
15
+ filename = original_filename || content.default_file_name
16
+ basename = [ File.basename(filename, ".*"), File.extname(filename) ]
17
+ infile = Tempfile.open(basename, Ddr::Models.tempdir, encoding: 'ascii-8bit')
18
+ begin
19
+ infile.write(content.content)
20
+ infile.close
21
+ verify_checksum!(infile)
22
+ yield infile.path
23
+ ensure
24
+ infile.close unless infile.closed?
25
+ File.unlink(infile)
26
+ end
27
+ end
28
+
29
+ def verify_checksum!(file)
30
+ digest = Ddr::Utils.digest(File.read(file), content.checksumType)
31
+ if digest != content.checksum
32
+ raise ChecksumInvalid, "The checksum of the downloaded file does not match the stored checksum of the content."
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -18,6 +18,9 @@
18
18
  property "eadId",
19
19
  label: "EAD ID"
20
20
 
21
+ property "archivesSpaceId",
22
+ label: "ArchivesSpace Identifier"
23
+
21
24
  end
22
25
  end
23
26
  end
@@ -17,6 +17,13 @@ module Ddr
17
17
  expect(File.size(output_file.path)).to be > 0
18
18
  end
19
19
  end
20
+ context "animated gif source" do
21
+ let(:source) { File.join(Ddr::Models::Engine.root, "spec", "fixtures", "arrow1rightred_e0.gif") }
22
+ it "should generate a non-empty file" do
23
+ generator.generate
24
+ expect(File.size(output_file.path)).to be > 0
25
+ end
26
+ end
20
27
  end
21
28
 
22
29
  end
@@ -34,7 +34,7 @@ FactoryGirl.define do
34
34
  sequence(:identifier) { |n| [ "testparent%05d" % n ] }
35
35
 
36
36
  factory :test_parent_has_children do
37
- ignore do
37
+ transient do
38
38
  child_count 3
39
39
  end
40
40
  after(:create) do |parent, evaluator|
Binary file
@@ -0,0 +1,33 @@
1
+ module Ddr::Index
2
+ RSpec.describe QueryValue do
3
+
4
+ describe ".or_values" do
5
+ describe "when argument is nil" do
6
+ it "raises an exception" do
7
+ expect { described_class.or_values(nil) }.to raise_error(ArgumentError)
8
+ end
9
+ end
10
+ describe "when argument is empty" do
11
+ it "raises an exception" do
12
+ expect { described_class.or_values([]) }.to raise_error(ArgumentError)
13
+ end
14
+ end
15
+ describe "when argument is not enumerable" do
16
+ it "raises an exception" do
17
+ expect { described_class.or_values("foo") }.to raise_error(ArgumentError)
18
+ end
19
+ end
20
+ describe "when argument size == 1" do
21
+ it "returns the first value, escaped" do
22
+ expect(described_class.or_values(["foo:bar"])).to eq("(foo\\:bar)")
23
+ end
24
+ end
25
+ describe "when argument size > 1" do
26
+ it "return the escaped values OR'd together in parentheses" do
27
+ expect(described_class.or_values(["foo:bar", "spam:eggs"])).to eq("(foo\\:bar OR spam\\:eggs)")
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -3,50 +3,14 @@ require 'spec_helper'
3
3
  module Ddr::Jobs
4
4
  RSpec.describe FitsFileCharacterization, jobs: true, file_characterization: true do
5
5
 
6
- shared_examples "has a fits update event" do
7
- let(:event) { object.update_events.last }
8
- it "should have the correct event attributes" do
9
- expect(event.outcome).to eq(expected_outcome)
10
- expect(event.detail).to eq(expected_detail)
11
- expect(event.software).to eq("fits #{fits_version}")
12
- end
13
- end
6
+ let(:obj) { double }
14
7
 
15
- context "content-bearing object" do
16
- let(:object) { TestContent.create }
17
- let(:stdout_msg) { '<fits />' }
18
- let(:stderr_msg) { 'stderr msg' }
19
- let(:fits_version) { '0.9.9 '}
20
- before { allow(Ddr::Jobs::FitsFileCharacterization).to receive(:fits_version) { fits_version } }
21
- context "fits command is successful" do
22
- let(:expected_outcome) { Ddr::Events::Event::SUCCESS }
23
- let(:expected_detail) { nil }
24
- before do
25
- allow(Open3).to receive(:capture3) { [ stdout_msg, stderr_msg, $? ] }
26
- allow_any_instance_of(Process::Status).to receive(:success?) { true }
27
- Ddr::Jobs::FitsFileCharacterization.perform(object.pid)
28
- object.reload
29
- end
30
- it "should populate the fits datastream" do
31
- expect(object.fits.content).to be_present
32
- end
33
- it_behaves_like "has a fits update event"
34
- end
35
- context "fits command is not successful" do
36
- let(:expected_outcome) { Ddr::Events::Event::FAILURE }
37
- let(:expected_detail) { stderr_msg }
38
- before do
39
- allow(Open3).to receive(:capture3) { [ stdout_msg, stderr_msg, $? ] }
40
- allow_any_instance_of(Process::Status).to receive(:success?) { false }
41
- Ddr::Jobs::FitsFileCharacterization.perform(object.pid)
42
- object.reload
43
- end
44
- it "should not populate the fits datastream" do
45
- expect(object.fits.content).to_not be_present
46
- end
47
- it_behaves_like "has a fits update event"
48
- end
49
- end
8
+ before { allow(ActiveFedora::Base).to receive(:find).with("test:1") { obj } }
9
+
10
+ specify {
11
+ expect(Ddr::Models::FileCharacterization).to receive(:call).with(obj) { nil }
12
+ described_class.perform("test:1")
13
+ }
50
14
 
51
15
  end
52
- end
16
+ end