Skip to content

Commit

Permalink
feat(resource): add ArchiveRepresentation to API V1 (DEV-393) (DEV-394)…
Browse files Browse the repository at this point in the history
… (#1984)

* add TODOs

* feat: implement archive representation in v1

* chore: tidy up according to PR review
  • Loading branch information
BalduinLandolt committed Jan 31, 2022
1 parent ab80e72 commit 65b88a2
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 9 deletions.
Expand Up @@ -81,6 +81,8 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) {
makeVideoFileValue(valueProps, projectShortcode, responderManager, userProfile)
case OntologyConstants.KnoraBase.DocumentFileValue =>
makeDocumentFileValue(valueProps, projectShortcode, responderManager, userProfile)
case OntologyConstants.KnoraBase.ArchiveFileValue =>
makeArchiveFileValue(valueProps, projectShortcode, responderManager, userProfile)
case OntologyConstants.KnoraBase.LinkValue => makeLinkValue(valueProps, responderManager, userProfile)
}
}
Expand All @@ -106,6 +108,15 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) {
def makeSipiDocumentGetUrlFromFilename(documentFileValueV1: DocumentFileValueV1): String =
s"${settings.externalSipiIIIFGetUrl}/${documentFileValueV1.projectShortcode}/${documentFileValueV1.internalFilename}/file"

/**
* Creates a URL for accessing a archive file via Sipi.
*
* @param archiveFileValueV1 the archive file value.
* @return a Sipi URL.
*/
def makeSipiArchiveGetUrlFromFilename(archiveFileValueV1: ArchiveFileValueV1): String =
s"${settings.externalSipiIIIFGetUrl}/${archiveFileValueV1.projectShortcode}/${archiveFileValueV1.internalFilename}/file"

/**
* Creates a URL for accessing a text file via Sipi.
*
Expand Down Expand Up @@ -197,6 +208,13 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) {
path = makeSipiDocumentGetUrlFromFilename(documentFileValueV1)
)

case archiveFileValueV1: ArchiveFileValueV1 =>
LocationV1(
format_name = mimeType2V1Format(archiveFileValueV1.internalMimeType),
origname = archiveFileValueV1.originalFilename,
path = makeSipiArchiveGetUrlFromFilename(archiveFileValueV1)
)

case textFileValue: TextFileValueV1 =>
LocationV1(
format_name = mimeType2V1Format(textFileValue.internalMimeType),
Expand Down Expand Up @@ -398,6 +416,8 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) {

case _: DocumentFileValueV1 => basicObjectResponse

case _: ArchiveFileValueV1 => basicObjectResponse

case _: AudioFileValueV1 => basicObjectResponse

case _: MovingImageFileValueV1 => basicObjectResponse
Expand Down Expand Up @@ -916,6 +936,30 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) {
)
}

/**
* Converts a [[ValueProps]] into a [[ArchiveFileValueV1]].
*
* @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted.
* @return a [[ArchiveFileValueV1]].
*/
private def makeArchiveFileValue(
valueProps: ValueProps,
projectShortcode: String,
responderManager: ActorRef,
userProfile: UserADM
)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = {
val predicates = valueProps.literalData

Future(
ArchiveFileValueV1(
internalMimeType = predicates(OntologyConstants.KnoraBase.InternalMimeType).literals.head,
internalFilename = predicates(OntologyConstants.KnoraBase.InternalFilename).literals.head,
originalFilename = predicates.get(OntologyConstants.KnoraBase.OriginalFilename).map(_.literals.head),
projectShortcode = projectShortcode
)
)
}

/**
* Converts a [[ValueProps]] into a [[AudioFileValueV1]].
*
Expand Down
Expand Up @@ -1611,6 +1611,64 @@ case class DocumentFileValueV1(
)
}

/**
* A representation of a compressed archive in a binary format.
*
* @param internalMimeType the MIME-type of the internal representation.
* @param internalFilename the internal filename of the object.
* @param originalFilename the original filename of the object at the time of the import.
*/
case class ArchiveFileValueV1(
internalMimeType: String,
internalFilename: String,
originalFilename: Option[String] = None,
originalMimeType: Option[String] = None,
projectShortcode: String
) extends FileValueV1 {
def valueTypeIri: IRI = OntologyConstants.KnoraBase.ArchiveFileValue

def toJsValue: JsValue = ApiValueV1JsonProtocol.archiveFileValueV1Format.write(this)

override def toString: String = internalFilename

/**
* Checks if a new archive file value would duplicate an existing archive file value.
*
* @param other another [[ValueV1]].
* @return `true` if `other` is a duplicate of `this`.
*/
override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean =
other match {
case archiveFileValueV1: ArchiveFileValueV1 => archiveFileValueV1 == this
case otherValue =>
throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}")
}

/**
* Checks if a new version of a archive file value would be redundant given the current version of the value.
*
* @param currentVersion the current version of the value.
* @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`.
*/
override def isRedundant(currentVersion: ApiValueV1): Boolean =
currentVersion match {
case archiveFileValueV1: ArchiveFileValueV1 => archiveFileValueV1 == this
case other =>
throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}")
}

override def toFileValueContentV2: FileValueContentV2 =
ArchiveFileValueContentV2(
ontologySchema = InternalSchema,
fileValue = FileValueV2(
internalFilename = internalFilename,
internalMimeType = internalMimeType,
originalFilename = originalFilename,
originalMimeType = Some(internalMimeType)
)
)
}

case class AudioFileValueV1(
internalMimeType: String,
internalFilename: String,
Expand Down Expand Up @@ -1861,6 +1919,7 @@ object ApiValueV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol
implicit val dateValueV1Format: JsonFormat[DateValueV1] = jsonFormat5(DateValueV1)
implicit val stillImageFileValueV1Format: JsonFormat[StillImageFileValueV1] = jsonFormat7(StillImageFileValueV1)
implicit val documentFileValueV1Format: JsonFormat[DocumentFileValueV1] = jsonFormat8(DocumentFileValueV1)
implicit val archiveFileValueV1Format: JsonFormat[ArchiveFileValueV1] = jsonFormat5(ArchiveFileValueV1)
implicit val textFileValueV1Format: JsonFormat[TextFileValueV1] = jsonFormat5(TextFileValueV1)
implicit val audioFileValueV1Format: JsonFormat[AudioFileValueV1] = jsonFormat6(AudioFileValueV1)
implicit val movingImageFileValueV1Format: JsonFormat[MovingImageFileValueV1] = jsonFormat9(MovingImageFileValueV1)
Expand Down
27 changes: 21 additions & 6 deletions webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV1.scala
Expand Up @@ -20,10 +20,11 @@ import org.knora.webapi.messages.store.sipimessages.GetFileMetadataResponse
import org.knora.webapi.messages.util.standoff.StandoffTagUtilV2
import org.knora.webapi.messages.util.standoff.StandoffTagUtilV2.TextWithStandoffTagsV2
import org.knora.webapi.messages.v1.responder.valuemessages.{
MovingImageFileValueV1,
ArchiveFileValueV1,
AudioFileValueV1,
DocumentFileValueV1,
FileValueV1,
MovingImageFileValueV1,
StillImageFileValueV1,
TextFileValueV1
}
Expand Down Expand Up @@ -258,11 +259,7 @@ object RouteUtilV1 {
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/zip",
"application/x-tar",
"application/x-iso9660-image",
"application/gzip"
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
)

/**
Expand All @@ -283,6 +280,16 @@ object RouteUtilV1 {
"video/mp4"
)

/**
* MIME types used in Sipi to store archive files.
*/
private val archiveMimeTypes: Set[String] = Set(
"application/zip",
"application/x-tar",
"application/gzip",
"application/x-7z-compressed"
)

/**
* Converts file metadata from Sipi into a [[FileValueV1]].
*
Expand Down Expand Up @@ -348,6 +355,14 @@ object RouteUtilV1 {
dimY =
fileMetadataResponse.height.getOrElse(throw SipiException(s"Sipi did not return the height of the video"))
)
} else if (archiveMimeTypes.contains(fileMetadataResponse.internalMimeType)) {
ArchiveFileValueV1(
internalFilename = filename,
internalMimeType = fileMetadataResponse.internalMimeType,
originalFilename = fileMetadataResponse.originalFilename,
originalMimeType = fileMetadataResponse.originalMimeType,
projectShortcode = projectShortcode
)
} else {
throw BadRequestException(s"MIME type ${fileMetadataResponse.internalMimeType} not supported in Knora API v1")
}
Expand Down
Expand Up @@ -364,6 +364,27 @@ DELETE {
}
}

case archiveFileValue: ArchiveFileValueV1 => {
?newValue knora-base:internalFilename """@archiveFileValue.internalFilename""" .
?newValue knora-base:internalMimeType """@archiveFileValue.internalMimeType""" .

@archiveFileValue.originalFilename match {
case Some(definedOriginalFilename) => {
?newValue knora-base:originalFilename """@definedOriginalFilename""" .
}

case None => {}
}

@archiveFileValue.originalMimeType match {
case Some(definedOriginalMimeType) => {
?newValue knora-base:originalMimeType """@definedOriginalMimeType""" .
}

case None => {}
}
}

case listValue: HierarchicalListValueV1 => {

?newValue knora-base:valueHasListNode <@listValue.hierarchicalListIri> .
Expand Down
Expand Up @@ -320,6 +320,27 @@
}
}

case archiveFileValue: ArchiveFileValueV1 => {
<@newValueIri> knora-base:internalFilename """@archiveFileValue.internalFilename""" ;
knora-base:internalMimeType """@archiveFileValue.internalMimeType""" .

@archiveFileValue.originalFilename match {
case Some(definedOriginalFilename) => {
<@newValueIri> knora-base:originalFilename """@definedOriginalFilename""" .
}

case None => {}
}

@archiveFileValue.originalMimeType match {
case Some(definedOriginalMimeType) => {
<@newValueIri> knora-base:originalMimeType """@definedOriginalMimeType""" .
}

case None => {}
}
}

case listValue: HierarchicalListValueV1 => {

<@newValueIri> knora-base:valueHasListNode <@listValue.hierarchicalListIri> .
Expand Down
Expand Up @@ -850,9 +850,9 @@ class KnoraSipiIntegrationV1ITSpec
uploadedZipFile.originalFilename should ===(minimalZipOriginalFilename)

// Create a resource for the Zip file.
val createDocumentResourceParams = JsObject(
val archiveResourceParams = JsObject(
Map(
"restype_id" -> JsString("http://www.knora.org/ontology/0001/anything#ThingDocument"),
"restype_id" -> JsString("http://www.knora.org/ontology/knora-base#ArchiveRepresentation"),
"label" -> JsString("Zip file"),
"project_id" -> JsString("http://rdfh.ch/projects/0001"),
"properties" -> JsObject(),
Expand All @@ -863,7 +863,7 @@ class KnoraSipiIntegrationV1ITSpec
// Send the JSON in a POST request to the Knora API server.
val createDocumentResourceRequest: HttpRequest = Post(
baseApiUrl + "/v1/resources",
HttpEntity(ContentTypes.`application/json`, createDocumentResourceParams.compactPrint)
HttpEntity(ContentTypes.`application/json`, archiveResourceParams.compactPrint)
) ~> addCredentials(BasicHttpCredentials(userEmail, password))

val createDocumentResourceResponseJson: JsObject = getResponseJson(createDocumentResourceRequest)
Expand Down

0 comments on commit 65b88a2

Please sign in to comment.