dpn-admin/dpn-bagit

View on GitHub
lib/dpn/bagit/bag.rb

Summary

Maintainability
C
1 day
Test Coverage
require "find"
require "dpn/bagit/settings"
require "dpn/bagit/ext/bagit"
require "dpn/bagit/uuidv4_validator"
require "dpn/bagit/bag/dpn_info_txt"

# A wrapper for an unserialized Bag-It bag on disk.  Does not support serialized bags.
# Once created, a Bag object will not change with changes made to the underlying filesystem
# bag; in that case, a new Bag object should be created.
class DPN::Bagit::Bag
    private
      def initialize(_location)
        @settings = DPN::Bagit::Settings.instance.config
        @bag = ::BagIt::Bag.new(_location)
        @location = _location
        @cachedValidity = nil
        @cachedFixity = nil
        @cachedSize = nil
        @validationErrors = []
        @dpnObjectID = nil

        @dpnInfo = DPNInfoTxt.new(self.dpn_info_file_location())
        @dpnObjectID = @dpnInfo[:dpnObjectID]
        @dpnObjectID ||= File.basename(_location)
    end


  public
    # Get the net fixity of the Bag.
    # @param algorithm [Symbol] Algorithm to use.
    # @return [String] The fixity of the tagmanifest-<alg>.txt file.
    def fixity(algorithm)
      if @cachedFixity == nil
        case algorithm
          when :sha256
            digest = Digest::SHA256
            path = File.join(@location, "tagmanifest-sha256.txt")
            if File.exists?(path)
              @cachedFixity = digest.file(path).hexdigest
            else
              @cachedFixity = ""
              @cachedValidity = false
            end
          else
            raise ArgumentError, "Unknown algorithm."
        end

      end
      return @cachedFixity
    end


    # Returns the total size of the Bag.
    # @return [Fixnum] Apparent size of the Bag in bytes.
    def size()
      if @cachedSize == nil
        size = 0
        Find.find(self.location) do |f|
          if File.file?(f) or File.directory?(f)
            size += File.size(f)
          end
        end
        @cachedSize = size
      end
      return @cachedSize
    end


    # Returns the local file location of the Bag.
    # @return [String] The location, which can be relative or absolute.
    def location()
      return @location
    end


    # Returns the uuid of the bag, according to dpn-info.txt.
    # @return [String]
    def uuid()
      return @dpnObjectID
    end


    # Checks that all required files are present, no extraneous files are present, and all file digests
    # match manifests.
    # @return [Boolean] True if valid, false otherwise.
    def valid?()
      if @cachedValidity == nil
        if @bag.valid? == false
          @validationErrors.push(@bag.errors.full_messages)
        end

        if File.exists?(@bag.fetch_txt_file) == true
          @validationErrors.push("The file fetch.txt is present and unsupported.")
        end

        if Pathname.new(@bag.bag_dir).basename.to_s != @dpnInfo[:dpnObjectID]
          @validationErrors.push("The name of the root directory does not match the #{@settings[:bag][:dpn_info][:dpnObjectID][:name]}.")
        end

        if File.exists?(@bag.manifest_file("sha256")) == true
          if File.readable?(@bag.manifest_file("sha256")) == false
            @validationErrors.push("The file manifest-sha256.txt exists but cannot be read.")
          end
        else
          @validationErrors.push("The file manifest-sha256.txt does not exist.")
        end

        if File.exists?(@bag.tagmanifest_file("sha256")) == true
          if File.readable?(@bag.tagmanifest_file("sha256")) == false
            @validationErrors.push("The file tagmanifest-sha256.txt exists but cannot be read.")
          end
        else
          @validationErrors.push("The file tagmanifest-sha256.txt does not exist.")
        end

        if @dpnInfo[:version].to_i <= 0
          @validationErrors.push("Version must be > 0.")
        end

        uuidValidator = DPN::Bagit::UUID4Validator.new(true)
        if uuidValidator.isValid?(@dpnInfo[:dpnObjectID]) == false
          @validationErrors.push("#{@settings[:bag][:dpn_info][:dpnObjectID][:name]} with value \"#{@dpnInfo[:dpnObjectID]}\" is not a valid UUIDv4.")
        end

        if uuidValidator.isValid?(@dpnInfo[:firstVersionObjectID]) == false
          @validationErrors.push("#{@settings[:bag][:dpn_info][:firstVersionObjectID][:name]} with value \"#{@dpnInfo[:firstVersionObjectID]}\" is not a valid UUIDv4.")
        end

        @dpnInfo[:rightsObjectIDs].each do |id|
          if uuidValidator.isValid?(id) == false
            @validationErrors.push("#{@settings[:bag][:dpn_info][:rightsObjectIDs][:name]} value of \"#{id}\" is not a valid UUIDv4.")
          end
        end

        @dpnInfo[:interpretiveObjectIDs].each do |id|
          if uuidValidator.isValid?(id) == false
            @validationErrors.push("#{@settings[:bag][:dpn_info][:interpretiveObjectIDs][:name]} value of \"#{id}\" is not a valid UUIDv4.")
          end
        end


        if @validationErrors.empty? == true and @dpnInfo.getErrors.empty? == true
          @cachedValidity = true
        else
          @cachedValidity = false
        end
      end

      return @cachedValidity
    end


    # Returns validation errors.  The list is not populated until a call to {#isValid?} has been made.
    # @return [Array<String>] The errors.
    def errors()
      return @dpnInfo.getErrors() + @validationErrors
    end


    # Returns true if the Bag contains no files.
    # @return [Boolean] True if empty, false otherwise.
    def empty?()
      return @bag.empty?
    end

  protected
    # Get the path of the dpn-info.txt file for this bag.
    # @return [String]
    def dpn_info_file_location()
      return File.join(@bag.bag_dir, @settings[:bag][:dpn_dir], @settings[:bag][:dpn_info][:name])
    end
end