AwsAssetMappingDto.java

/*
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License
 * at:
 *
 *   http://opensource.org/licenses/ecl2.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package org.opencastproject.assetmanager.aws.persistence;

import static org.opencastproject.db.Queries.namedQuery;

import org.opencastproject.assetmanager.api.storage.StoragePath;

import org.apache.commons.lang3.tuple.Pair;

import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import javax.persistence.UniqueConstraint;
import javax.xml.bind.annotation.XmlAttribute;

@Entity(name = "AwsAssetMapping")
@Table(
    name = "oc_aws_asset_mapping",
    indexes = {
        @Index(
            name = "IX_oc_aws_asset_mapping_object_key",
            columnList = ("object_key")
        )
    },
    uniqueConstraints = @UniqueConstraint(
        name = "UNQ_aws_archive_mapping_0",
        columnNames = {"organization", "mediapackage", "mediapackage_element", "version"}
    )
)
@NamedQueries({
    // These exclude deleted mappings!
    @NamedQuery(
        name = "AwsAssetMapping.findActiveMapping",
        query = "SELECT m FROM AwsAssetMapping m WHERE m.organizationId = :organizationId AND "
            + "m.mediaPackageId = :mediaPackageId AND m.mediaPackageElementId = :mediaPackageElementId AND "
            + "m.version = :version AND m.deletionDate IS NULL"
    ),
    @NamedQuery(
        name = "AwsAssetMapping.findAllActiveByObjectKey",
        query = "SELECT m FROM AwsAssetMapping m WHERE m.objectKey = :objectKey AND m.deletionDate IS NULL"
    ),
    @NamedQuery(
        name = "AwsAssetMapping.findAllActiveByMediaPackage",
        query = "SELECT m FROM AwsAssetMapping m WHERE m.organizationId = :organizationId AND "
            + "m.mediaPackageId = :mediaPackageId  AND m.deletionDate IS NULL"
    ),
    @NamedQuery(
        name = "AwsAssetMapping.findAllActiveByMediaPackageAndVersion",
        query = "SELECT m FROM AwsAssetMapping m WHERE m.organizationId = :organizationId AND "
            + "m.mediaPackageId = :mediaPackageId AND m.version = :version AND m.deletionDate IS NULL"
    ),
    // This is to be used when restoring/re-ingesting mps so includes deleted mapings!
    @NamedQuery(
        name = "AwsAssetMapping.findAllByMediaPackage",
        query = "SELECT m FROM AwsAssetMapping m WHERE m.mediaPackageId = :mediaPackageId ORDER BY m.version DESC"
    )
})
public class AwsAssetMappingDto {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "id", length = 128)
  @XmlAttribute
  private long id;

  @Column(name = "organization", nullable = false, length = 128)
  private String organizationId;

  @Column(name = "mediapackage", nullable = false, length = 128)
  private String mediaPackageId;

  @Column(name = "mediapackage_element", nullable = false, length = 128)
  private String mediaPackageElementId;

  @Column(name = "version", nullable = false)
  private Long version;

  // This is the AWS object key
  @Column(name = "object_key", nullable = false, length = 1024)
  private String objectKey;

  // This is the AWS object version
  @Column(name = "object_version", nullable = false, length = 1024)
  private String objectVersion;

  @Column(name = "deletion_date")
  @Temporal(TemporalType.TIMESTAMP)
  private Date deletionDate;

  public AwsAssetMappingDto() {
  }

  /** Constructor with all fields. */
  public AwsAssetMappingDto(String organizationId, String mediaPackageId, String elementId, Long version,
          String objectKey, String objectVersion) {
    this.organizationId = organizationId;
    this.mediaPackageId = mediaPackageId;
    this.mediaPackageElementId = elementId;
    this.version = version;
    this.objectKey = objectKey;
    this.objectVersion = objectVersion;
  }

  /** Convert into business object. */
  public AwsAssetMapping toAWSArchiveMapping() {
    return new AwsAssetMapping(organizationId, mediaPackageId, mediaPackageElementId, version, objectKey,
            objectVersion, deletionDate);
  }

  public static Function<EntityManager, AwsAssetMappingDto> storeMappingQuery(StoragePath path, String objectKey,
      String objectVersion) {
    return em -> {
      AwsAssetMappingDto mapDto = new AwsAssetMappingDto(
          path.getOrganizationId(),
          path.getMediaPackageId(),
          path.getMediaPackageElementId(),
          Long.valueOf(path.getVersion().toString()),
          objectKey,
          objectVersion
      );

      Optional<AwsAssetMappingDto> existing = findMappingQuery(path).apply(em);

      // If we've already seen this at some point but deleted it, just undelete it
      if (existing.isPresent() && objectKey.equals(existing.get().objectKey)
          && objectVersion.equals(existing.get().objectVersion)) {
        existing.get().setDeletionDate(null);
        return existing.get();
      } else {
        em.persist(mapDto);
        return mapDto;
      }
    };
  }

  /** Find a mapping by its storage path. Returns null if not found. */
  public static Function<EntityManager, Optional<AwsAssetMappingDto>> findMappingQuery(final StoragePath path) {
    return namedQuery.findOpt(
        "AwsAssetMapping.findActiveMapping",
        AwsAssetMappingDto.class,
        Pair.of("organizationId", path.getOrganizationId()),
        Pair.of("mediaPackageId", path.getMediaPackageId()),
        Pair.of("mediaPackageElementId", path.getMediaPackageElementId()),
        Pair.of("version", Long.valueOf(path.getVersion().toString()))
    );
  }

  /** Find all assets that link to the AWS S3 object passed. */
  public static Function<EntityManager, List<AwsAssetMappingDto>> findMappingsByKeyQuery(final String objectKey) {
    return namedQuery.findAll(
        "AwsAssetMapping.findAllActiveByObjectKey",
        AwsAssetMappingDto.class,
        Pair.of("objectKey", objectKey)
    );
  }

  /** Find all assets that belong to a media package and version (optional). */
  public static Function<EntityManager, List<AwsAssetMappingDto>> findMappingsByMediaPackageAndVersionQuery(
      final StoragePath path) {
    return em -> {
      // Find a specific versions?
      TypedQuery<AwsAssetMappingDto> query;
      if (path.getVersion() != null) {
        query = em.createNamedQuery("AwsAssetMapping.findAllActiveByMediaPackageAndVersion",
            AwsAssetMappingDto.class);
        query.setParameter("version", Long.valueOf(path.getVersion().toString()));
      } else {
        query = em.createNamedQuery("AwsAssetMapping.findAllActiveByMediaPackage", AwsAssetMappingDto.class);
      }

      query.setParameter("organizationId", path.getOrganizationId());
      query.setParameter("mediaPackageId", path.getMediaPackageId());

      return query.getResultList();
    };
  }

  /**
   * Marks mapping as deleted.
   */
  public static Consumer<EntityManager> deleteMapppingQuery(StoragePath path) {
    return em -> {
      Optional<AwsAssetMappingDto> mapDto = findMappingQuery(path).apply(em);
      if (mapDto.isEmpty()) {
        return;
      }
      mapDto.get().setDeletionDate(new Date());
      em.merge(mapDto.get());
    };
  }

  /** Find all mappings that belong to a media package id. Also returns deleted mappings! */
  public static Function<EntityManager, List<AwsAssetMappingDto>> findMappingsByMediaPackageQuery(final String mpId) {
    return namedQuery.findAll(
        "AwsAssetMapping.findAllByMediaPackage",
        AwsAssetMappingDto.class,
        Pair.of("mediaPackageId", mpId)
    );
  }

  public void setDeletionDate(Date deletionDate) {
    this.deletionDate = deletionDate;
  }
}