Entity Client

Methods to create, get and update entities in Preservica. The methods correspond to calls in the Preservica entity API.

Creating a client

The client takes mandatory arguments for the url of the Preservica service and the secret name of the secrets manager secret storing the API credentials.

There is a caching layer which caches the secret value and the API token on disk. The duration of the cache defaults to 15 minutes but can be configured here.

It is also possible to configure the secrets manager endpoint. This can be used for testing or if using private endpoints in a VPC.

These examples use both the FS2 client and the ZIO client

object EntityClients {
  import cats.effect.IO
  import sttp.capabilities.fs2.Fs2Streams
  import uk.gov.nationalarchives.dp.client.EntityClient
  import uk.gov.nationalarchives.dp.client.fs2.Fs2Client

  import scala.concurrent.duration.*

  val preservicaUrl = "https://test.preservica.com"
  val secretName = "nameOfSecretsManagerSecretContainingAPICredentials"

  val fs2EntityClientWithDefaults: IO[EntityClient[IO, Fs2Streams[IO]]] =
    Fs2Client.entityClient(preservicaUrl, secretName)
  val fs2EntityClientWithCustomCacheDuration: IO[EntityClient[IO, Fs2Streams[IO]]] =
    Fs2Client.entityClient(preservicaUrl, secretName, 30.minutes)
  val fs2EntityClientWithCustomSecretsManagerEndpoint: IO[EntityClient[IO, Fs2Streams[IO]]] =
    Fs2Client.entityClient(preservicaUrl, secretName, ssmEndpointUri = "https://private.ssm.endpoint")
  val fs2EntityClientWithCustomProxy: IO[EntityClient[IO, Fs2Streams[IO]]] =
    Fs2Client.entityClient(preservicaUrl, secretName, potentialProxyUrl = Option(URI.create("http://proxy.url")))
}

The client exposes 15 methods

  def metadataForEntity(entity: Entity): F[Seq[Elem]]

  def getBitstreamInfo(contentObjectRef: UUID): F[Seq[BitStreamInfo]]

  def getEntity(entityRef: UUID, entityType: EntityType): F[Entity]

  def getEntityIdentifiers(entity: Entity): F[Seq[IdentifierResponse]]

  def getUrlsToIoRepresentations(ioEntityRef: UUID, representationType: Option[RepresentationType]): F[Seq[String]]

  def addEntity(addEntityRequest: AddEntityRequest): F[UUID]

  def updateEntity(updateEntityRequest: UpdateEntityRequest): F[String]

  def updateEntityIdentifiers(entity: Entity, identifiers: Seq[IdentifierResponse]): F[Seq[IdentifierResponse]]

  def streamBitstreamContent[T](
      stream: Streams[S]
  )(url: String, streamFn: stream.BinaryStream => F[T]): F[T]

  def entitiesUpdatedSince(
      dateTime: ZonedDateTime,
      startEntry: Int,
      maxEntries: Int = 1000
  ): F[Seq[Entity]]

  def entityEventActions(
      entity: Entity,
      startEntry: Int = 0,
      maxEntries: Int = 1000
  ): F[Seq[EventAction]]

  def entitiesByIdentifier(
      identifier: Identifier
  ): F[Seq[Entity]]

  def getContentObjectsFromRepresentation(
      ioEntityRef: UUID,
      representationType: RepresentationType,
      repTypeIndex: Int
  ): F[Seq[Entity]]

  def addIdentifierForEntity(
      entityRef: UUID,
      entityType: EntityType,
      identifier: Identifier
  ): F[String]

  def getPreservicaNamespaceVersion(
      endpoint: String
  ): F[Float]

Method descriptions

These are descriptions of the steps taken by each method. All methods get an authentication token first, either from the API or the cache.

metadataForEntity

  • Get the entity for the provided reference
  • Get the fragment urls from "AdditionalInformation" \ "Metadata" \ "Fragment"
  • For each fragment url, get the response
  • Return a list of the XML elements found at "MetadataContainer" \ "Content" in each response

getBitstreamInfo

  • Get the entity for the provided reference
  • Get the first generation url from "AdditionalInformation" \ "Generations"
  • Call the API with this url
  • Get all the generations from "Generations" \ "Generation"
  • Send an API request for each of these urls
  • Get the bitstream url from "Bitstreams" \ "Bitstream" for each of the responses
  • Call the API with each of these bitstream urls.
  • Get the file name, file size and download url for the bitstream from each response.

getEntity

  • Get the entity from the provided reference
  • Parse the response into the Entity class

getEntityIdentifiers

  • Get the entity identifiers from the reference
  • Call the API for each url returned in the previous step.
  • Check for a next page. If there is one, return to step 2 until there is no next page, otherwise, return a Seq[IdentifierResponse]

getUrlsToIoRepresentations

  • Use the IO’s ref in the endpoint’s url
  • Call the API to receive a RepresentationsResponse.
  • Get all the representations from "Representations" \ "Representation"
  • Filter the representations on the optional RepresentationType
  • Retrieve url(s) from remaining representations

getContentObjectsFromRepresentation

  • Use the IO’s ref, representationType and version in the endpoint’s url
  • Call the API to receive a RepresentationsResponse.
  • Get all the Representation’s Content Objects from "Representation" \ "ContentObjects" \ "ContentObject"
  • Wrap Content Objects in an Entity

addEntity

  • Convert the AddEntityRequest case class into XML
  • Send the XML to the API to create an entity.
  • Parse the response and return the newly created reference UUID.

updateEntity

  • Convert the UpdateEntityRequest case class into XML
  • Send the XML to the API to update an entity.
  • Parse the response and return “Entity was updated”

updateEntityIdentifiers

  • Convert the Seq[IdentifierResponse] into XML
  • Send the XML to the API to update the entity identifiers.
  • Parse the response and return the original identifiers argument.

streamBitstreamContent

  • Calls the url from the first argument.
  • Passes a stream of the response to the function provided by the second argument.

entitiesUpdatedSince

  • Calls the updated-since endpoint using the parameters in the arguments.
  • Converts the response to Seq[Entity] and returns

entityEventActions

  • Calls the event-actions endpoint using the parameters in the arguments.
  • If there is a next page in the result, call the API until the last page is reached.
  • Converts the response to Seq[EventAction] and returns

entitiesByIdentifier

  • Calls the by-identifier endpoint using the parameters in the arguments.
  • The entities returned by this endpoint don’t contain all the information we need. So for each entity returned, call the /{ref} endpoint

addIdentifierForEntity

  • Converts the arguments to the XML input
  • Calls the identifiers endpoint to create an identifier for an entity

getPreservicaNamespaceVersion

  • Calls any Entity Preservica endpoint that returns XML
  • Returns version number found in namespace