All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openmetadata.service.jdbi3.UsageRepository Maven / Gradle / Ivy

There is a newer version: 1.5.11
Show newest version
/*
 *  Copyright 2021 Collate
 *  Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
 *  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.openmetadata.service.jdbi3;

import static org.openmetadata.schema.type.EventType.ENTITY_FIELDS_CHANGED;
import static org.openmetadata.schema.type.EventType.ENTITY_UPDATED;
import static org.openmetadata.service.Entity.CHART;
import static org.openmetadata.service.Entity.DASHBOARD;
import static org.openmetadata.service.Entity.FIELD_USAGE_SUMMARY;
import static org.openmetadata.service.Entity.MLMODEL;
import static org.openmetadata.service.Entity.TABLE;
import static org.openmetadata.service.util.EntityUtil.fieldUpdated;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import javax.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;
import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.statement.StatementContext;
import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.schema.EntityInterface;
import org.openmetadata.schema.entity.data.Chart;
import org.openmetadata.schema.entity.data.Dashboard;
import org.openmetadata.schema.entity.data.MlModel;
import org.openmetadata.schema.entity.data.Table;
import org.openmetadata.schema.type.ChangeDescription;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.DailyCount;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.EntityUsage;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.UsageDetails;
import org.openmetadata.schema.type.UsageStats;
import org.openmetadata.service.Entity;
import org.openmetadata.service.exception.CatalogExceptionMessage;
import org.openmetadata.service.exception.UnhandledServerException;
import org.openmetadata.service.util.RestUtil;

@Slf4j
@Repository
public class UsageRepository {
  private static final String PUT = "createOrUpdate";
  private static final String POST = "createNew";
  private final CollectionDAO dao;

  public UsageRepository() {
    this.dao = Entity.getCollectionDAO();
    Entity.setUsageRepository(this);
  }

  public EntityUsage get(String entityType, UUID id, String date, int days) {
    EntityReference ref = Entity.getEntityReferenceById(entityType, id, Include.NON_DELETED);
    List usageDetails = dao.usageDAO().getUsageById(id, date, days - 1);
    return new EntityUsage().withUsage(usageDetails).withEntity(ref);
  }

  public EntityUsage getByName(String entityType, String fqn, String date, int days) {
    EntityReference ref = Entity.getEntityReferenceByName(entityType, fqn, Include.NON_DELETED);
    List usageDetails = dao.usageDAO().getUsageById(ref.getId(), date, days - 1);
    return new EntityUsage().withUsage(usageDetails).withEntity(ref);
  }

  @Transaction
  public RestUtil.PutResponse create(String entityType, UUID id, DailyCount usage) {
    // Validate data entity for which usage is being collected
    Entity.getEntityReferenceById(entityType, id, Include.NON_DELETED);
    return addUsage(POST, entityType, id, usage);
  }

  @Transaction
  public RestUtil.PutResponse createByName(
      String entityType, String fullyQualifiedName, DailyCount usage) {
    EntityReference ref =
        Entity.getEntityReferenceByName(entityType, fullyQualifiedName, Include.NON_DELETED);
    return addUsage(POST, entityType, ref.getId(), usage);
  }

  @Transaction
  public RestUtil.PutResponse createOrUpdate(String entityType, UUID id, DailyCount usage) {
    // Validate data entity for which usage is being collected
    Entity.getEntityReferenceById(entityType, id, Include.NON_DELETED);
    return addUsage(PUT, entityType, id, usage);
  }

  @Transaction
  public RestUtil.PutResponse createOrUpdateByName(
      String entityType, String fullyQualifiedName, DailyCount usage) {
    EntityReference ref =
        Entity.getEntityReferenceByName(entityType, fullyQualifiedName, Include.NON_DELETED);
    return addUsage(PUT, entityType, ref.getId(), usage);
  }

  @Transaction
  public void computePercentile(String entityType, String date) {
    dao.usageDAO().computePercentile(entityType, date);
  }

  private RestUtil.PutResponse addUsage(
      String method, String entityType, UUID entityId, DailyCount usage) {
    String fields = "usageSummary";
    // If table usage was reported, add the usage count to schema and database
    String type = entityType.toLowerCase();
    switch (type) {
      case TABLE:
        return tableEntityUsage(method, fields, entityId, entityType, usage);
      case DASHBOARD:
        return dashboardEntityUsage(method, fields, entityId, entityType, usage);
      case CHART:
        return chartEntityUsage(method, fields, entityId, entityType, usage);
      case MLMODEL:
        return mlModelEntityUsage(method, fields, entityId, entityType, usage);
      default:
        LOG.error("Invalid Usage Entity Type");
        throw new UnhandledServerException(
            CatalogExceptionMessage.entityTypeNotSupported(entityType));
    }
  }

  private RestUtil.PutResponse tableEntityUsage(
      String method, String fields, UUID entityId, String entityType, DailyCount usage) {
    // we accept usage for deleted entities
    Table table = Entity.getEntity(Entity.TABLE, entityId, fields, Include.ALL);
    // Insert usage record
    insertToUsageRepository(method, entityId, entityType, usage);
    Table updated = Entity.getEntity(Entity.TABLE, entityId, fields, Include.ALL);
    dao.usageDAO()
        .insertOrUpdateCount(
            usage.getDate(),
            table.getDatabaseSchema().getId(),
            Entity.DATABASE_SCHEMA,
            usage.getCount());
    dao.usageDAO()
        .insertOrUpdateCount(
            usage.getDate(), table.getDatabase().getId(), Entity.DATABASE, usage.getCount());

    ChangeDescription change =
        getChangeDescription(
            table.getVersion(), updated.getUsageSummary(), table.getUsageSummary());
    ChangeEvent changeEvent = getChangeEvent(updated, change, entityType, table.getVersion());

    return new RestUtil.PutResponse<>(Response.Status.CREATED, changeEvent, ENTITY_FIELDS_CHANGED);
  }

  private RestUtil.PutResponse dashboardEntityUsage(
      String method, String fields, UUID entityId, String entityType, DailyCount usage) {
    Dashboard dashboard = Entity.getEntity(Entity.DASHBOARD, entityId, fields, Include.ALL);
    insertToUsageRepository(method, entityId, entityType, usage);
    Dashboard updated = Entity.getEntity(Entity.DASHBOARD, entityId, fields, Include.ALL);

    ChangeDescription change =
        getChangeDescription(
            dashboard.getVersion(), updated.getUsageSummary(), dashboard.getUsageSummary());
    ChangeEvent changeEvent = getChangeEvent(updated, change, entityType, dashboard.getVersion());

    return new RestUtil.PutResponse<>(Response.Status.CREATED, changeEvent, ENTITY_FIELDS_CHANGED);
  }

  private RestUtil.PutResponse chartEntityUsage(
      String method, String fields, UUID entityId, String entityType, DailyCount usage) {
    Chart chart = Entity.getEntity(Entity.CHART, entityId, fields, Include.ALL);
    insertToUsageRepository(method, entityId, entityType, usage);
    Chart updated = Entity.getEntity(Entity.CHART, entityId, fields, Include.ALL);

    ChangeDescription change =
        getChangeDescription(
            chart.getVersion(), updated.getUsageSummary(), chart.getUsageSummary());
    ChangeEvent changeEvent = getChangeEvent(updated, change, entityType, chart.getVersion());

    return new RestUtil.PutResponse<>(Response.Status.CREATED, changeEvent, ENTITY_FIELDS_CHANGED);
  }

  private RestUtil.PutResponse mlModelEntityUsage(
      String method, String fields, UUID entityId, String entityType, DailyCount usage) {
    MlModel mlModel = Entity.getEntity(Entity.MLMODEL, entityId, fields, Include.ALL);
    insertToUsageRepository(method, entityId, entityType, usage);
    MlModel updated = Entity.getEntity(Entity.CHART, entityId, fields, Include.ALL);

    ChangeDescription change =
        getChangeDescription(
            mlModel.getVersion(), updated.getUsageSummary(), mlModel.getUsageSummary());
    ChangeEvent changeEvent = getChangeEvent(updated, change, entityType, mlModel.getVersion());

    return new RestUtil.PutResponse<>(Response.Status.CREATED, changeEvent, ENTITY_FIELDS_CHANGED);
  }

  private void insertToUsageRepository(
      String method, UUID entityId, String entityType, DailyCount usage) {
    if (method.equals(POST)) {
      dao.usageDAO().insertOrReplaceCount(usage.getDate(), entityId, entityType, usage.getCount());
    } else if (method.equals(PUT)) {
      dao.usageDAO().insertOrUpdateCount(usage.getDate(), entityId, entityType, usage.getCount());
    }
  }

  private ChangeEvent getChangeEvent(
      EntityInterface updated, ChangeDescription change, String entityType, Double prevVersion) {
    return new ChangeEvent()
        .withId(UUID.randomUUID())
        .withEntity(updated)
        .withChangeDescription(change)
        .withEventType(ENTITY_UPDATED)
        .withEntityType(entityType)
        .withEntityId(updated.getId())
        .withEntityFullyQualifiedName(updated.getFullyQualifiedName())
        .withUserName(updated.getUpdatedBy())
        .withTimestamp(System.currentTimeMillis())
        .withCurrentVersion(updated.getVersion())
        .withPreviousVersion(prevVersion);
  }

  private ChangeDescription getChangeDescription(Double version, Object newValue, Object oldValue) {
    ChangeDescription change = new ChangeDescription().withPreviousVersion(version);
    fieldUpdated(change, FIELD_USAGE_SUMMARY, oldValue, newValue);
    return change;
  }

  public static class UsageDetailsMapper implements RowMapper {
    @Override
    public UsageDetails map(ResultSet r, StatementContext ctx) throws SQLException {
      UsageStats dailyStats =
          new UsageStats()
              .withCount(r.getInt("count1"))
              .withPercentileRank(r.getDouble("percentile1"));
      UsageStats weeklyStats =
          new UsageStats()
              .withCount(r.getInt("count7"))
              .withPercentileRank(r.getDouble("percentile7"));
      UsageStats monthlyStats =
          new UsageStats()
              .withCount(r.getInt("count30"))
              .withPercentileRank(r.getDouble("percentile30"));
      return new UsageDetails()
          .withDate(r.getString("usageDate"))
          .withDailyStats(dailyStats)
          .withWeeklyStats(weeklyStats)
          .withMonthlyStats(monthlyStats);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy