mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-06 15:43:04 +00:00
parent
b4d56bb559
commit
d978580703
@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory;
|
||||
public class CatalogGenericExceptionMapper implements ExceptionMapper<Throwable> {
|
||||
@Override
|
||||
public Response toResponse(Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
LOG.debug(ex.getMessage());
|
||||
if (ex instanceof ProcessingException
|
||||
|| ex instanceof IllegalArgumentException
|
||||
|
@ -88,10 +88,10 @@ public class UsageRepository {
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public RestUtil.PutResponse<?> createOrUpdate(String entityType, String id, DailyCount usage) throws IOException {
|
||||
public RestUtil.PutResponse<?> createOrUpdate(String entityType, UUID id, DailyCount usage) throws IOException {
|
||||
// Validate data entity for which usage is being collected
|
||||
Entity.getEntityReferenceById(entityType, UUID.fromString(id), Include.NON_DELETED);
|
||||
return addUsage(PUT, entityType, id, usage);
|
||||
Entity.getEntityReferenceById(entityType, id, Include.NON_DELETED);
|
||||
return addUsage(PUT, entityType, id.toString(), usage);
|
||||
}
|
||||
|
||||
@Transaction
|
||||
|
@ -494,7 +494,6 @@ public class SearchResource {
|
||||
@Operation(
|
||||
operationId = "getAllReindexBatchJobs",
|
||||
summary = "Get all reindex batch jobs",
|
||||
tags = "search",
|
||||
description = "Get all reindex batch jobs",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", description = "Success"),
|
||||
|
@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import javax.validation.Valid;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
@ -34,15 +35,20 @@ import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openmetadata.schema.type.DailyCount;
|
||||
import org.openmetadata.schema.type.EntityUsage;
|
||||
import org.openmetadata.schema.type.MetadataOperation;
|
||||
import org.openmetadata.service.Entity;
|
||||
import org.openmetadata.service.jdbi3.CollectionDAO;
|
||||
import org.openmetadata.service.jdbi3.UsageRepository;
|
||||
import org.openmetadata.service.resources.Collection;
|
||||
import org.openmetadata.service.resources.EntityResource;
|
||||
import org.openmetadata.service.security.Authorizer;
|
||||
import org.openmetadata.service.security.policyevaluator.OperationContext;
|
||||
import org.openmetadata.service.security.policyevaluator.ResourceContext;
|
||||
import org.openmetadata.service.util.RestUtil;
|
||||
|
||||
@Slf4j
|
||||
@ -53,9 +59,11 @@ import org.openmetadata.service.util.RestUtil;
|
||||
@Collection(name = "usage")
|
||||
public class UsageResource {
|
||||
private final UsageRepository dao;
|
||||
private final Authorizer authorizer;
|
||||
|
||||
public UsageResource(CollectionDAO dao, Authorizer authorizer) {
|
||||
Objects.requireNonNull(dao, "UsageRepository must not be null");
|
||||
this.authorizer = authorizer;
|
||||
this.dao = new UsageRepository(dao);
|
||||
}
|
||||
|
||||
@ -75,6 +83,7 @@ public class UsageResource {
|
||||
})
|
||||
public EntityUsage get(
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@Parameter(
|
||||
description = "Entity type for which usage is requested",
|
||||
required = true,
|
||||
@ -93,7 +102,10 @@ public class UsageResource {
|
||||
@QueryParam("date")
|
||||
String date)
|
||||
throws IOException {
|
||||
// TODO add href
|
||||
OperationContext operationContext = new OperationContext(entity, MetadataOperation.VIEW_USAGE);
|
||||
ResourceContext resourceContext =
|
||||
EntityResource.getResourceContext(entity, Entity.getEntityRepository(entity)).build();
|
||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||
int actualDays = Math.min(Math.max(days, 1), 30);
|
||||
String actualDate = date == null ? RestUtil.DATE_FORMAT.format(new Date()) : date;
|
||||
return addHref(uriInfo, dao.get(entity, id, actualDate, actualDays));
|
||||
@ -115,6 +127,7 @@ public class UsageResource {
|
||||
})
|
||||
public EntityUsage getByName(
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@Parameter(
|
||||
description = "Entity type for which usage is requested",
|
||||
required = true,
|
||||
@ -135,8 +148,12 @@ public class UsageResource {
|
||||
description =
|
||||
"Usage for number of days going back from this date in ISO 8601 format " + "(default = currentDate)")
|
||||
@QueryParam("date")
|
||||
String date) {
|
||||
// TODO add href
|
||||
String date)
|
||||
throws IOException {
|
||||
OperationContext operationContext = new OperationContext(entity, MetadataOperation.VIEW_USAGE);
|
||||
ResourceContext resourceContext =
|
||||
EntityResource.getResourceContext(entity, Entity.getEntityRepository(entity)).name(fqn).build();
|
||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||
int actualDays = Math.min(Math.max(days, 1), 30);
|
||||
String actualDate = date == null ? RestUtil.DATE_FORMAT.format(new Date()) : date;
|
||||
return addHref(uriInfo, dao.getByName(entity, fqn, actualDate, actualDays));
|
||||
@ -159,6 +176,7 @@ public class UsageResource {
|
||||
})
|
||||
public Response create(
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@Parameter(
|
||||
description = "Entity type for which usage is reported",
|
||||
required = true,
|
||||
@ -169,6 +187,10 @@ public class UsageResource {
|
||||
String id,
|
||||
@Parameter(description = "Usage information a given date") @Valid DailyCount usage)
|
||||
throws IOException {
|
||||
OperationContext operationContext = new OperationContext(entity, MetadataOperation.EDIT_USAGE);
|
||||
ResourceContext resourceContext =
|
||||
EntityResource.getResourceContext(entity, Entity.getEntityRepository(entity)).build();
|
||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||
return dao.create(entity, id, usage).toResponse();
|
||||
}
|
||||
|
||||
@ -189,6 +211,7 @@ public class UsageResource {
|
||||
})
|
||||
public Response createOrUpdate(
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@Parameter(
|
||||
description = "Entity type for which usage is reported",
|
||||
required = true,
|
||||
@ -196,9 +219,13 @@ public class UsageResource {
|
||||
@PathParam("entity")
|
||||
String entity,
|
||||
@Parameter(description = "Entity id", required = true, schema = @Schema(type = "string")) @PathParam("id")
|
||||
String id,
|
||||
UUID id,
|
||||
@Parameter(description = "Usage information a given date") @Valid DailyCount usage)
|
||||
throws IOException {
|
||||
OperationContext operationContext = new OperationContext(entity, MetadataOperation.EDIT_USAGE);
|
||||
ResourceContext resourceContext =
|
||||
EntityResource.getResourceContext(entity, Entity.getEntityRepository(entity)).id(id).build();
|
||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||
return dao.createOrUpdate(entity, id, usage).toResponse();
|
||||
}
|
||||
|
||||
@ -219,6 +246,7 @@ public class UsageResource {
|
||||
})
|
||||
public Response createByName(
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@Parameter(
|
||||
description = "Entity type for which usage is reported",
|
||||
required = true,
|
||||
@ -233,6 +261,10 @@ public class UsageResource {
|
||||
String fullyQualifiedName,
|
||||
@Parameter(description = "Usage information a given date") @Valid DailyCount usage)
|
||||
throws IOException {
|
||||
OperationContext operationContext = new OperationContext(entity, MetadataOperation.EDIT_USAGE);
|
||||
ResourceContext resourceContext =
|
||||
EntityResource.getResourceContext(entity, Entity.getEntityRepository(entity)).name(fullyQualifiedName).build();
|
||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||
return dao.createByName(entity, fullyQualifiedName, usage).toResponse();
|
||||
}
|
||||
|
||||
@ -253,6 +285,7 @@ public class UsageResource {
|
||||
})
|
||||
public Response createOrUpdateByName(
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@Parameter(
|
||||
description = "Entity type for which usage is reported",
|
||||
required = true,
|
||||
@ -267,6 +300,10 @@ public class UsageResource {
|
||||
String fullyQualifiedName,
|
||||
@Parameter(description = "Usage information a given date") @Valid DailyCount usage)
|
||||
throws IOException {
|
||||
OperationContext operationContext = new OperationContext(entity, MetadataOperation.EDIT_USAGE);
|
||||
ResourceContext resourceContext =
|
||||
EntityResource.getResourceContext(entity, Entity.getEntityRepository(entity)).name(fullyQualifiedName).build();
|
||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||
return dao.createOrUpdateByName(entity, fullyQualifiedName, usage).toResponse();
|
||||
}
|
||||
|
||||
@ -283,6 +320,7 @@ public class UsageResource {
|
||||
})
|
||||
public Response computePercentile(
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@Parameter(
|
||||
description = "Entity name for which usage is requested",
|
||||
schema = @Schema(type = "string", example = "table, report, metrics, or dashboard"))
|
||||
@ -292,8 +330,12 @@ public class UsageResource {
|
||||
description = "ISO 8601 format date to compute percentile on",
|
||||
schema = @Schema(type = "string", example = "2021-01-28"))
|
||||
@PathParam("date")
|
||||
String date) {
|
||||
// TODO delete this?
|
||||
String date)
|
||||
throws IOException {
|
||||
OperationContext operationContext = new OperationContext(entity, MetadataOperation.EDIT_USAGE);
|
||||
ResourceContext resourceContext =
|
||||
EntityResource.getResourceContext(entity, Entity.getEntityRepository(entity)).build();
|
||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||
dao.computePercentile(entity, date);
|
||||
return Response.status(Response.Status.CREATED).build();
|
||||
}
|
||||
|
@ -17,11 +17,16 @@ import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
|
||||
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.openmetadata.common.utils.CommonUtil.getDateStringByOffset;
|
||||
import static org.openmetadata.common.utils.CommonUtil.listOf;
|
||||
import static org.openmetadata.service.Entity.INGESTION_BOT_NAME;
|
||||
import static org.openmetadata.service.Entity.TABLE;
|
||||
import static org.openmetadata.service.exception.CatalogExceptionMessage.entityNotFound;
|
||||
import static org.openmetadata.service.exception.CatalogExceptionMessage.entityTypeNotFound;
|
||||
import static org.openmetadata.service.security.SecurityUtil.authHeaders;
|
||||
import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS;
|
||||
import static org.openmetadata.service.util.TestUtils.NON_EXISTENT_ENTITY;
|
||||
import static org.openmetadata.service.util.TestUtils.TEST_AUTH_HEADERS;
|
||||
import static org.openmetadata.service.util.TestUtils.TEST_USER_NAME;
|
||||
import static org.openmetadata.service.util.TestUtils.assertResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -34,6 +39,7 @@ import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.HttpResponseException;
|
||||
@ -49,9 +55,11 @@ import org.openmetadata.schema.entity.data.Database;
|
||||
import org.openmetadata.schema.entity.data.Table;
|
||||
import org.openmetadata.schema.type.DailyCount;
|
||||
import org.openmetadata.schema.type.EntityUsage;
|
||||
import org.openmetadata.schema.type.MetadataOperation;
|
||||
import org.openmetadata.schema.type.UsageDetails;
|
||||
import org.openmetadata.service.Entity;
|
||||
import org.openmetadata.service.OpenMetadataApplicationTest;
|
||||
import org.openmetadata.service.exception.CatalogExceptionMessage;
|
||||
import org.openmetadata.service.resources.databases.DatabaseResourceTest;
|
||||
import org.openmetadata.service.resources.databases.TableResourceTest;
|
||||
import org.openmetadata.service.util.RestUtil;
|
||||
@ -148,25 +156,51 @@ class UsageResourceTest extends OpenMetadataApplicationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void post_validUsageByName_200_OK(TestInfo test) {
|
||||
testValidUsageByName(test, POST);
|
||||
void post_validUsageByNameAsAdmin_200(TestInfo test) {
|
||||
testValidUsageByName(test, POST, ADMIN_AUTH_HEADERS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void put_validUsageByName_200_OK(TestInfo test) {
|
||||
testValidUsageByName(test, PUT);
|
||||
void post_validUsageByNameAsIngestionBot_200(TestInfo test) {
|
||||
testValidUsageByName(test, POST, authHeaders(INGESTION_BOT_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
void post_validUsageByNameAsNonPrivilegedUser_401(TestInfo test) {
|
||||
assertResponse(
|
||||
() -> testValidUsageByName(test, POST, TEST_AUTH_HEADERS),
|
||||
Status.FORBIDDEN,
|
||||
CatalogExceptionMessage.permissionNotAllowed(TEST_USER_NAME, listOf(MetadataOperation.EDIT_USAGE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void put_validUsageByNameAsAdmin_200(TestInfo test) {
|
||||
testValidUsageByName(test, PUT, ADMIN_AUTH_HEADERS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void put_validUsageByNameAsIngestionBot_200(TestInfo test) {
|
||||
testValidUsageByName(test, PUT, authHeaders(INGESTION_BOT_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
void put_validUsageByNameAsNonPrivilegedUser_401(TestInfo test) {
|
||||
assertResponse(
|
||||
() -> testValidUsageByName(test, PUT, TEST_AUTH_HEADERS),
|
||||
Status.FORBIDDEN,
|
||||
CatalogExceptionMessage.permissionNotAllowed(TEST_USER_NAME, listOf(MetadataOperation.EDIT_USAGE)));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
void testValidUsageByName(TestInfo test, String methodType) {
|
||||
void testValidUsageByName(TestInfo test, String methodType, Map<String, String> authHeaders) {
|
||||
TableResourceTest tableResourceTest = new TableResourceTest();
|
||||
Table table = tableResourceTest.createEntity(tableResourceTest.createRequest(test), ADMIN_AUTH_HEADERS);
|
||||
DailyCount usageReport = usageReport().withCount(100).withDate(RestUtil.DATE_FORMAT.format(new Date()));
|
||||
reportUsageByNameAndCheckPut(TABLE, table.getFullyQualifiedName(), usageReport, 100, 100, ADMIN_AUTH_HEADERS);
|
||||
reportUsageByNameAndCheckPut(TABLE, table.getFullyQualifiedName(), usageReport, 100, 100, authHeaders);
|
||||
// a put request updates the data again
|
||||
if (methodType.equals(PUT)) {
|
||||
reportUsageByNamePut(TABLE, table.getFullyQualifiedName(), usageReport, ADMIN_AUTH_HEADERS);
|
||||
checkUsageByName(usageReport.getDate(), TABLE, table.getFullyQualifiedName(), 200, 200, 200, ADMIN_AUTH_HEADERS);
|
||||
reportUsageByNamePut(TABLE, table.getFullyQualifiedName(), usageReport, authHeaders);
|
||||
checkUsageByName(usageReport.getDate(), TABLE, table.getFullyQualifiedName(), 200, 200, 200, authHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
"EditTeams",
|
||||
"EditTier",
|
||||
"EditTests",
|
||||
"EditUsage",
|
||||
"EditUsers"
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user