Skip to content

Commit

Permalink
Complete and validate (#13)
Browse files Browse the repository at this point in the history
* isComplete implementation

* IntelliJ formatting

* isValid implementation

* fix compiler warning

* make docs more clear

* bullit list in scaladoc

* virtually valid type definition + documentation

* clarify documentation on isComplete and isValid

* formatting + imports

* isVirtuallyValid is no longer part of this issue

* formatting

* extra tests for isComplete and isValid

* formatting
  • Loading branch information
rvanheest authored Aug 7, 2018
1 parent 73cdce6 commit 31c63a9
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 8 deletions.
35 changes: 35 additions & 0 deletions src/main/scala/nl/knaw/dans/bag/DansBag.scala
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,41 @@ trait DansBag {
* `scala.util.Failure` otherwise
*/
def save(): Try[Unit]

/**
* Verifies if a bag is complete.
* According to the BagIt version 16 specs, a bag is complete when:
* - bagit.txt is present
* - data/ directory is present
* - at least one payload manifest exists
* - all fetch items are present in the bag
* - all files in every payload manifest must be present
* - all files in every tag manifest must be present
* - (>= V1.0 bag) all files in the payload directory are listed in all payload manifests
* - (< V1.0 bag) all files in the payload directory are listed in at least one payload manifest
*
* @return either `Unit` (if the bag is complete) or `String` (containing the error message if
* the bag is not complete)
*/
def isComplete: Either[String, Unit]

/**
* Verifies if a bag is valid.
* According to the BagIt v16 specs, a bag is valid when:
* - the bag is complete
* - every checksum in every payload manifest has been successfully verified against the
* contents of the corresponding file
* - every checksum in every tag manifest has been successfully verified against the
* contents of the corresponding file
*
* Due to calculating the checksums for all files, this method may take some time to complete and
* return. It is therefore strongly advised to wrap a call to this method in a `Promise`/`Future`,
* `Observable` or any other desired data structure that deals with latency in a proper way.
*
* @return either `Unit` (if the bag is valid) or `String` (containing the error message if
* the bag is not valid)
*/
def isValid: Either[String, Unit]
}

object DansBag {
Expand Down
28 changes: 24 additions & 4 deletions src/main/scala/nl/knaw/dans/bag/v0/DansV0Bag.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import java.util.{ UUID, Set => jSet }

import better.files.{ CloseableOps, Disposable, File, Files, ManagedResource }
import gov.loc.repository.bagit.creator.BagCreator
import gov.loc.repository.bagit.domain.{ Version, Bag => LocBag, Manifest => LocManifest, Metadata => LocMetadata, FetchItem => LocFetchItem }
import gov.loc.repository.bagit.domain.{ Version, Bag => LocBag, FetchItem => LocFetchItem, Manifest => LocManifest, Metadata => LocMetadata }
import gov.loc.repository.bagit.reader.BagReader
import gov.loc.repository.bagit.util.PathUtils
import gov.loc.repository.bagit.verify.BagVerifier
import gov.loc.repository.bagit.writer.{ BagitFileWriter, FetchWriter, ManifestWriter, MetadataWriter }
import nl.knaw.dans.bag.ChecksumAlgorithm.{ ChecksumAlgorithm, locDeconverter }
import nl.knaw.dans.bag.{ ChecksumAlgorithm, DansBag, FetchItem, RelativePath, betterFileToPath }
Expand Down Expand Up @@ -185,7 +186,7 @@ class DansV0Bag private(private[v0] val locBag: LocBag) extends DansBag {
.map(_.asScala)
.collect {
case Seq(userId) => userId
case userIds if userIds.size > 1 => throw new IllegalStateException(s"Only one EASY-User-Account allowed; found ${userIds.size}")
case userIds if userIds.size > 1 => throw new IllegalStateException(s"Only one EASY-User-Account allowed; found ${ userIds.size }")
}
}

Expand All @@ -209,7 +210,9 @@ class DansV0Bag private(private[v0] val locBag: LocBag) extends DansBag {
/**
* @inheritdoc
*/
override def fetchFiles: Seq[FetchItem] = locBag.getItemsToFetch.asScala.map(fetch => fetch: FetchItem)
override def fetchFiles: Seq[FetchItem] = {
locBag.getItemsToFetch.asScala.map(fetch => fetch: FetchItem)
}

/**
* @inheritdoc
Expand Down Expand Up @@ -453,7 +456,8 @@ class DansV0Bag private(private[v0] val locBag: LocBag) extends DansBag {
/**
* @inheritdoc
*/
override def addPayloadFile(inputStream: InputStream)(pathInData: RelativePath): Try[DansV0Bag] = Try {
override def addPayloadFile(inputStream: InputStream)
(pathInData: RelativePath): Try[DansV0Bag] = Try {
val file = pathInData(data)

if (file.exists)
Expand Down Expand Up @@ -633,6 +637,22 @@ class DansV0Bag private(private[v0] val locBag: LocBag) extends DansBag {
ManifestWriter.writeTagManifests(locBag.getTagManifests, baseDir, baseDir, fileEncoding)
}

/**
* @inheritdoc
*/
override def isComplete: Either[String, Unit] = {
Try { new ManagedResource(new BagVerifier()).apply(_.isComplete(this.locBag, false)) }
.toEither.left.map(_.getMessage)
}

/**
* @inheritdoc
*/
override def isValid: Either[String, Unit] = {
Try { new ManagedResource(new BagVerifier()).apply(_.isValid(this.locBag, false)) }
.toEither.left.map(_.getMessage)
}

protected def validateURL(url: URL): Unit = {
if (url.getProtocol != "http" && url.getProtocol != "https")
throw new IllegalArgumentException("url can only have protocol 'http' or 'https'")
Expand Down
10 changes: 10 additions & 0 deletions src/test/scala/nl/knaw/dans/bag/fixtures/FetchFileMetadata.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ package nl.knaw.dans.bag.fixtures

import java.net.URL

import better.files.File
import nl.knaw.dans.bag.RelativePath

trait FetchFileMetadata {
this: TestSupportFixture =>

def findFile(pathRelativeToTestResources: String): File = {
File(getClass.getResource(pathRelativeToTestResources))
}

val lipsum1File: File = findFile("/fetch-files/lipsum1.txt")
val lipsum1URL: URL = new URL("https://raw.githubusercontent.com/rvanheest-DANS-KNAW/" +
"dans-bag-lib/c8681a5932e4081dfec95680abefc9a07740a97a/src/test/resources/fetch-files/" +
"lipsum1.txt")
Expand All @@ -33,6 +39,7 @@ trait FetchFileMetadata {
"19d5cdbb6f0f84be9145643ac793dd63eaee407e51d6dacd5ba3c1d8f6fe07da"
val lipsum1Size: Long = 485L

val lipsum2File: File = findFile("/fetch-files/lipsum2.txt")
val lipsum2URL: URL = new URL("https://raw.githubusercontent.com/rvanheest-DANS-KNAW/" +
"dans-bag-lib/c8681a5932e4081dfec95680abefc9a07740a97a/src/test/resources/fetch-files/" +
"lipsum2.txt")
Expand All @@ -44,6 +51,7 @@ trait FetchFileMetadata {
"d4086396119e2ebaadd79548d623d8dfa350f854af6271da68311abb1c0e73e8"
val lipsum2Size: Long = 989L

val lipsum3File: File = findFile("/fetch-files/lipsum3.txt")
val lipsum3URL: URL = new URL("https://raw.githubusercontent.com/rvanheest-DANS-KNAW/" +
"dans-bag-lib/c8681a5932e4081dfec95680abefc9a07740a97a/src/test/resources/fetch-files/" +
"lipsum3.txt")
Expand All @@ -55,6 +63,7 @@ trait FetchFileMetadata {
"647c11784b75eed3a20ac60efda146f040daeaa3837fa3aba9a2b94e30da0965"
val lipsum3Size: Long = 1641L

val lipsum4File: File = findFile("/fetch-files/lipsum4.txt")
val lipsum4URL: URL = new URL("https://raw.githubusercontent.com/rvanheest-DANS-KNAW/" +
"dans-bag-lib/c8681a5932e4081dfec95680abefc9a07740a97a/src/test/resources/fetch-files/" +
"lipsum4.txt")
Expand All @@ -66,6 +75,7 @@ trait FetchFileMetadata {
"e7e3639cba8c3a864fea8a6f58fc0a37310b3f4550466d8ff2c9ba0835bb3231"
val lipsum4Size: Long = 2095L

val lipsum5File: File = findFile("/fetch-files/lipsum5.txt")
val lipsum5URL: URL = new URL("https://raw.githubusercontent.com/rvanheest-DANS-KNAW/" +
"dans-bag-lib/c8681a5932e4081dfec95680abefc9a07740a97a/src/test/resources/fetch-files/" +
"lipsum5.txt")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ trait TestSupportFixture extends FlatSpec
with Matchers
with Inside
with OptionValues
with EitherValues
with Inspectors
Loading

0 comments on commit 31c63a9

Please sign in to comment.