mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-29 01:32:01 +00:00
Add Chart and Dashboard Service entities
This commit is contained in:
parent
0e4a3b26f9
commit
3244960083
@ -17,13 +17,11 @@
|
||||
package org.openmetadata.catalog.jdbi3;
|
||||
|
||||
import org.openmetadata.catalog.entity.data.Chart;
|
||||
import org.openmetadata.catalog.entity.data.Database;
|
||||
import org.openmetadata.catalog.entity.data.Table;
|
||||
import org.openmetadata.catalog.entity.services.DashboardService;
|
||||
import org.openmetadata.catalog.exception.CatalogExceptionMessage;
|
||||
import org.openmetadata.catalog.exception.EntityNotFoundException;
|
||||
import org.openmetadata.catalog.jdbi3.TeamRepository.TeamDAO;
|
||||
import org.openmetadata.catalog.jdbi3.UserRepository.UserDAO;
|
||||
import org.openmetadata.catalog.resources.charts.ChartResource;
|
||||
import org.openmetadata.catalog.resources.dashboards.DashboardResource;
|
||||
import org.openmetadata.catalog.Entity;
|
||||
import org.openmetadata.catalog.entity.data.Dashboard;
|
||||
@ -59,6 +57,9 @@ public abstract class DashboardRepository {
|
||||
private static final Fields DASHBOARD_PATCH_FIELDS = new Fields(DashboardResource.FIELD_LIST,
|
||||
"owner,service,tags,charts");
|
||||
|
||||
public static String getFQN(EntityReference service, Dashboard dashboard) {
|
||||
return (service.getName() + "." + dashboard.getName());
|
||||
}
|
||||
|
||||
@CreateSqlObject
|
||||
abstract DashboardDAO dashboardDAO();
|
||||
@ -111,6 +112,12 @@ public abstract class DashboardRepository {
|
||||
return dashboards;
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public Dashboard getByName(String fqn, Fields fields) throws IOException {
|
||||
Dashboard dashboard = EntityUtil.validate(fqn, dashboardDAO().findByFQN(fqn), Dashboard.class);
|
||||
return setFields(dashboard, fields);
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public Dashboard create(Dashboard dashboard, EntityReference service, EntityReference owner) throws IOException {
|
||||
getService(service); // Validate service
|
||||
@ -120,7 +127,8 @@ public abstract class DashboardRepository {
|
||||
@Transaction
|
||||
public PutResponse<Dashboard> createOrUpdate(Dashboard updatedDashboard, EntityReference service,
|
||||
EntityReference newOwner) throws IOException {
|
||||
String fqn = service.getName() + "." + updatedDashboard.getName();
|
||||
getService(service); // Validate service
|
||||
String fqn = getFQN(service, updatedDashboard);
|
||||
Dashboard storedDashboard = JsonUtils.readValue(dashboardDAO().findByFQN(fqn), Dashboard.class);
|
||||
if (storedDashboard == null) {
|
||||
return new PutResponse<>(Status.CREATED, createInternal(updatedDashboard, service, newOwner));
|
||||
@ -134,11 +142,12 @@ public abstract class DashboardRepository {
|
||||
|
||||
// Update owner relationship
|
||||
setFields(storedDashboard, DASHBOARD_UPDATE_FIELDS); // First get the ownership information
|
||||
updateRelationships(storedDashboard, updatedDashboard);
|
||||
updateOwner(storedDashboard, storedDashboard.getOwner(), newOwner);
|
||||
|
||||
// Service can't be changed in update since service name is part of FQN and
|
||||
// change to a different service will result in a different FQN and creation of a new database under the new service
|
||||
storedDashboard.setService(service);
|
||||
applyTags(updatedDashboard);
|
||||
|
||||
return new PutResponse<>(Response.Status.OK, storedDashboard);
|
||||
}
|
||||
@ -216,16 +225,24 @@ public abstract class DashboardRepository {
|
||||
return dashboard;
|
||||
}
|
||||
|
||||
private EntityReference getService(Dashboard dashboard) {
|
||||
private EntityReference getService(Dashboard dashboard) throws IOException {
|
||||
return dashboard == null ? null : getService(EntityUtil.getService(relationshipDAO(), dashboard.getId()));
|
||||
}
|
||||
|
||||
private EntityReference getService(EntityReference service) {
|
||||
// TODO What are the dashboard services?
|
||||
private EntityReference getService(EntityReference service) throws IOException {
|
||||
String id = service.getId().toString();
|
||||
if (service.getType().equalsIgnoreCase(Entity.DASHBOARD_SERVICE)) {
|
||||
DashboardService serviceInstance = EntityUtil.validate(id, dashboardServiceDAO().findById(id),
|
||||
DashboardService.class);
|
||||
service.setDescription(serviceInstance.getDescription());
|
||||
service.setName(serviceInstance.getName());
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Invalid service type %s for the chart", service.getType()));
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
public void setService(Dashboard dashboard, EntityReference service) {
|
||||
public void setService(Dashboard dashboard, EntityReference service) throws IOException {
|
||||
if (service != null && dashboard != null) {
|
||||
getService(service); // Populate service details
|
||||
relationshipDAO().insert(service.getId().toString(), dashboard.getId().toString(), service.getType(),
|
||||
@ -303,9 +320,11 @@ public abstract class DashboardRepository {
|
||||
private void addRelationships(Dashboard dashboard) throws IOException {
|
||||
// Add relationship from dashboard to chart
|
||||
String dashboardId = dashboard.getId().toString();
|
||||
for (EntityReference chart: dashboard.getCharts()) {
|
||||
relationshipDAO().insert(dashboardId, chart.getId().toString(), Entity.DASHBOARD, Entity.CHART,
|
||||
Relationship.CONTAINS.ordinal());
|
||||
if (dashboard.getCharts() != null) {
|
||||
for (EntityReference chart : dashboard.getCharts()) {
|
||||
relationshipDAO().insert(dashboardId, chart.getId().toString(), Entity.DASHBOARD, Entity.CHART,
|
||||
Relationship.CONTAINS.ordinal());
|
||||
}
|
||||
}
|
||||
// Add owner relationship
|
||||
EntityUtil.setOwner(relationshipDAO(), dashboard.getId(), Entity.DASHBOARD, dashboard.getOwner());
|
||||
@ -314,14 +333,6 @@ public abstract class DashboardRepository {
|
||||
applyTags(dashboard);
|
||||
}
|
||||
|
||||
private void updateRelationships(Dashboard origDashboard, Dashboard updatedDashboard) throws IOException {
|
||||
// Add owner relationship
|
||||
origDashboard.setOwner(getOwner(origDashboard));
|
||||
EntityUtil.updateOwner(relationshipDAO(), origDashboard.getOwner(), updatedDashboard.getOwner(),
|
||||
origDashboard.getId(), Entity.TABLE);
|
||||
applyTags(updatedDashboard);
|
||||
}
|
||||
|
||||
private Dashboard validateDashboard(String id) throws IOException {
|
||||
return EntityUtil.validate(id, dashboardDAO().findById(id), Dashboard.class);
|
||||
}
|
||||
@ -351,7 +362,7 @@ public abstract class DashboardRepository {
|
||||
List<String> listBefore(@Bind("fqnPrefix") String fqnPrefix, @Bind("limit") int limit,
|
||||
@Bind("before") String before);
|
||||
|
||||
@SqlQuery("SELECT json FROM chart_entity WHERE " +
|
||||
@SqlQuery("SELECT json FROM dashboard_entity WHERE " +
|
||||
"(fullyQualifiedName LIKE CONCAT(:fqnPrefix, '.%') OR :fqnPrefix IS NULL) AND " +
|
||||
"fullyQualifiedName > :after " +
|
||||
"ORDER BY fullyQualifiedName " +
|
||||
|
||||
@ -92,7 +92,7 @@ public abstract class UsageRepository {
|
||||
@Transaction
|
||||
public EntityUsage getByName(String entityType, String fqn, String date, int days) throws IOException {
|
||||
EntityReference ref = EntityUtil.getEntityReferenceByName(entityType, fqn, tableDAO(), databaseDAO(),
|
||||
metricsDAO(), reportDAO(), topicDAO(), chartDAO());
|
||||
metricsDAO(), reportDAO(), topicDAO(), chartDAO(), dashboardDAO());
|
||||
List<UsageDetails> usageDetails = usageDAO().getUsageById(ref.getId().toString(), date, days - 1);
|
||||
return new EntityUsage().withUsage(usageDetails).withEntity(ref);
|
||||
}
|
||||
@ -108,7 +108,7 @@ public abstract class UsageRepository {
|
||||
@Transaction
|
||||
public void createByName(String entityType, String fullyQualifiedName, DailyCount usage) throws IOException {
|
||||
EntityReference ref = EntityUtil.getEntityReferenceByName(entityType, fullyQualifiedName, tableDAO(),
|
||||
databaseDAO(), metricsDAO(), reportDAO(), topicDAO(), chartDAO());
|
||||
databaseDAO(), metricsDAO(), reportDAO(), topicDAO(), chartDAO(), dashboardDAO());
|
||||
addUsage(entityType, ref.getId().toString(), usage);
|
||||
LOG.info("Usage successfully posted by name");
|
||||
}
|
||||
|
||||
@ -98,6 +98,7 @@ public class ChartResource {
|
||||
EntityUtil.addHref(uriInfo, chart.getOwner());
|
||||
EntityUtil.addHref(uriInfo, chart.getService());
|
||||
EntityUtil.addHref(uriInfo, chart.getFollowers());
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ import org.openmetadata.catalog.entity.data.Dashboard;
|
||||
import org.openmetadata.catalog.jdbi3.DashboardRepository;
|
||||
import org.openmetadata.catalog.resources.Collection;
|
||||
import org.openmetadata.catalog.security.SecurityUtil;
|
||||
import org.openmetadata.catalog.type.EntityReference;
|
||||
import org.openmetadata.catalog.util.EntityUtil;
|
||||
import org.openmetadata.catalog.util.EntityUtil.Fields;
|
||||
import org.openmetadata.catalog.util.RestUtil;
|
||||
@ -77,20 +78,30 @@ import java.util.UUID;
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Collection(name = "dashboards", repositoryClass = "org.openmetadata.catalog.jdbi3.DashboardRepository")
|
||||
public class DashboardResource {
|
||||
public static final String COLLECTION_PATH = "/v1/dashboards/";
|
||||
public static final String DASHBOARD_COLLECTION_PATH = "v1/dashboards/";
|
||||
private final List<String> attributes = RestUtil.getAttributes(Dashboard.class);
|
||||
private final List<String> relationships = RestUtil.getAttributes(Dashboard.class);
|
||||
|
||||
private final DashboardRepository dao;
|
||||
private final CatalogAuthorizer authorizer;
|
||||
|
||||
private static List<Dashboard> addHref(UriInfo uriInfo, List<Dashboard> dashboards) {
|
||||
public static void addHref(UriInfo uriInfo, EntityReference ref) {
|
||||
ref.withHref(RestUtil.getHref(uriInfo, DASHBOARD_COLLECTION_PATH, ref.getId()));
|
||||
}
|
||||
|
||||
public static List<Dashboard> addHref(UriInfo uriInfo, List<Dashboard> dashboards) {
|
||||
Optional.ofNullable(dashboards).orElse(Collections.emptyList()).forEach(i -> addHref(uriInfo, i));
|
||||
return dashboards;
|
||||
}
|
||||
|
||||
private static Dashboard addHref(UriInfo uriInfo, Dashboard dashboard) {
|
||||
dashboard.setHref(RestUtil.getHref(uriInfo, COLLECTION_PATH, dashboard.getId()));
|
||||
public static Dashboard addHref(UriInfo uriInfo, Dashboard dashboard) {
|
||||
dashboard.setHref(RestUtil.getHref(uriInfo, DASHBOARD_COLLECTION_PATH, dashboard.getId()));
|
||||
EntityUtil.addHref(uriInfo, dashboard.getOwner());
|
||||
EntityUtil.addHref(uriInfo, dashboard.getService());
|
||||
if (dashboard.getCharts() != null) {
|
||||
EntityUtil.addHref(uriInfo, dashboard.getCharts());
|
||||
}
|
||||
EntityUtil.addHref(uriInfo, dashboard.getFollowers());
|
||||
return dashboard;
|
||||
}
|
||||
|
||||
@ -113,7 +124,7 @@ public class DashboardResource {
|
||||
}
|
||||
}
|
||||
|
||||
static final String FIELDS = "owner,service,followers,tags,usageSummary";
|
||||
static final String FIELDS = "owner,service,charts,followers,tags,usageSummary";
|
||||
public static final List<String> FIELD_LIST = Arrays.asList(FIELDS.replaceAll(" ", "")
|
||||
.split(","));
|
||||
|
||||
@ -196,6 +207,27 @@ public class DashboardResource {
|
||||
return addHref(uriInfo, dao.get(id, fields));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/name/{fqn}")
|
||||
@Operation(summary = "Get a dashboard by name", tags = "dashboards",
|
||||
description = "Get a dashboard by fully qualified name.",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", description = "The dashboard",
|
||||
content = @Content(mediaType = "application/json",
|
||||
schema = @Schema(implementation = Chart.class))),
|
||||
@ApiResponse(responseCode = "404", description = "Dashboard for instance {id} is not found")
|
||||
})
|
||||
public Dashboard getByName(@Context UriInfo uriInfo, @PathParam("fqn") String fqn,
|
||||
@Context SecurityContext securityContext,
|
||||
@Parameter(description = "Fields requested in the returned resource",
|
||||
schema = @Schema(type = "string", example = FIELDS))
|
||||
@QueryParam("fields") String fieldsParam) throws IOException {
|
||||
Fields fields = new Fields(FIELD_LIST, fieldsParam);
|
||||
Dashboard dashboard = dao.getByName(fqn, fields);
|
||||
return addHref(uriInfo, dashboard);
|
||||
}
|
||||
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Create a dashboard", tags = "dashboards",
|
||||
description = "Create a new dashboard.",
|
||||
@ -209,9 +241,10 @@ public class DashboardResource {
|
||||
@Valid CreateDashboard create) throws IOException {
|
||||
SecurityUtil.checkAdminOrBotRole(authorizer, securityContext);
|
||||
Dashboard dashboard = new Dashboard().withId(UUID.randomUUID()).withName(create.getName())
|
||||
.withService(create.getService()).withCharts(create.getCharts())
|
||||
.withDashboardUrl(create.getDashboardUrl()).withTags(create.getTags());
|
||||
addHref(uriInfo, dao.create(dashboard, dashboard.getService(), dashboard.getOwner()));
|
||||
.withDescription(create.getDescription()).withService(create.getService()).withCharts(create.getCharts())
|
||||
.withDashboardUrl(create.getDashboardUrl()).withTags(create.getTags())
|
||||
.withOwner(create.getOwner());
|
||||
dashboard = addHref(uriInfo, dao.create(dashboard, dashboard.getService(), dashboard.getOwner()));
|
||||
return Response.created(dashboard.getHref()).entity(dashboard).build();
|
||||
}
|
||||
|
||||
@ -249,15 +282,17 @@ public class DashboardResource {
|
||||
schema = @Schema(implementation = Dashboard.class))),
|
||||
@ApiResponse(responseCode = "400", description = "Bad request")
|
||||
})
|
||||
public Response createOrUpdate(@Context UriInfo uriInfo, @Context SecurityContext securityContext,
|
||||
@Valid Dashboard create) throws IOException {
|
||||
public Response createOrUpdate(@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@Valid CreateDashboard create) throws IOException {
|
||||
Dashboard dashboard = new Dashboard().withId(UUID.randomUUID()).withName(create.getName())
|
||||
.withService(create.getService()).withCharts(create.getCharts())
|
||||
.withDashboardUrl(create.getDashboardUrl()).withTags(create.getTags());
|
||||
addHref(uriInfo, dao.create(dashboard, dashboard.getService(), dashboard.getOwner()));
|
||||
.withDescription(create.getDescription()).withService(create.getService()).withCharts(create.getCharts())
|
||||
.withDashboardUrl(create.getDashboardUrl()).withTags(create.getTags())
|
||||
.withOwner(create.getOwner());
|
||||
|
||||
PutResponse<Dashboard> response = dao.createOrUpdate(dashboard, dashboard.getService(), dashboard.getOwner());
|
||||
addHref(uriInfo, response.getEntity());
|
||||
return Response.status(response.getStatus()).entity(response.getEntity()).build();
|
||||
dashboard = addHref(uriInfo, response.getEntity());
|
||||
return Response.status(response.getStatus()).entity(dashboard).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
|
||||
@ -45,6 +45,7 @@ import org.openmetadata.catalog.jdbi3.TopicRepository.TopicDAO;
|
||||
import org.openmetadata.catalog.jdbi3.UsageRepository.UsageDAO;
|
||||
import org.openmetadata.catalog.jdbi3.UserRepository.UserDAO;
|
||||
import org.openmetadata.catalog.resources.charts.ChartResource;
|
||||
import org.openmetadata.catalog.resources.dashboards.DashboardResource;
|
||||
import org.openmetadata.catalog.resources.databases.DatabaseResource;
|
||||
import org.openmetadata.catalog.resources.databases.TableResource;
|
||||
import org.openmetadata.catalog.resources.feeds.MessageParser.EntityLink;
|
||||
@ -135,6 +136,9 @@ public final class EntityUtil {
|
||||
case Entity.CHART:
|
||||
ChartResource.addHref(uriInfo, ref);
|
||||
break;
|
||||
case Entity.DASHBOARD:
|
||||
DashboardResource.addHref(uriInfo, ref);
|
||||
break;
|
||||
case Entity.MESSAGING_SERVICE:
|
||||
MessagingServiceResource.addHref(uriInfo, ref);
|
||||
break;
|
||||
@ -273,7 +277,8 @@ public final class EntityUtil {
|
||||
|
||||
public static EntityReference getEntityReferenceByName(String entity, String fqn, TableDAO tableDAO,
|
||||
DatabaseDAO databaseDAO, MetricsDAO metricsDAO,
|
||||
ReportDAO reportDAO, TopicDAO topicDAO, ChartDAO chartDAO)
|
||||
ReportDAO reportDAO, TopicDAO topicDAO, ChartDAO chartDAO,
|
||||
DashboardDAO dashboardDAO)
|
||||
throws IOException {
|
||||
if (entity.equalsIgnoreCase(Entity.TABLE)) {
|
||||
Table instance = EntityUtil.validate(fqn, tableDAO.findByFQN(fqn), Table.class);
|
||||
@ -299,6 +304,10 @@ public final class EntityUtil {
|
||||
Chart instance = EntityUtil.validate(fqn, chartDAO.findByFQN(fqn), Chart.class);
|
||||
return new EntityReference().withId(instance.getId()).withName(instance.getName()).withType(Entity.CHART)
|
||||
.withDescription(instance.getDescription());
|
||||
} else if (entity.equalsIgnoreCase(Entity.DASHBOARD)) {
|
||||
Dashboard instance = EntityUtil.validate(fqn, dashboardDAO.findByFQN(fqn), Dashboard.class);
|
||||
return new EntityReference().withId(instance.getId()).withName(instance.getName()).withType(Entity.DASHBOARD)
|
||||
.withDescription(instance.getDescription());
|
||||
}
|
||||
throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entityNotFound(entity, fqn));
|
||||
}
|
||||
@ -331,6 +340,9 @@ public final class EntityUtil {
|
||||
} else if (clazz.toString().toLowerCase().endsWith(Entity.CHART.toLowerCase())) {
|
||||
Chart instance = (Chart) entity;
|
||||
return getEntityReference(instance);
|
||||
} else if (clazz.toString().toLowerCase().endsWith(Entity.DASHBOARD.toLowerCase())) {
|
||||
Dashboard instance = (Dashboard) entity;
|
||||
return getEntityReference(instance);
|
||||
} else if (clazz.toString().toLowerCase().endsWith(Entity.MESSAGING_SERVICE.toLowerCase())) {
|
||||
MessagingService instance = (MessagingService) entity;
|
||||
return getEntityReference(instance);
|
||||
|
||||
@ -0,0 +1,668 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.catalog.resources.dashboards;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.apache.http.client.HttpResponseException;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.openmetadata.catalog.CatalogApplicationTest;
|
||||
import org.openmetadata.catalog.Entity;
|
||||
import org.openmetadata.catalog.api.data.CreateDashboard;
|
||||
import org.openmetadata.catalog.api.services.CreateDashboardService;
|
||||
import org.openmetadata.catalog.api.services.CreateDashboardService.DashboardServiceType;
|
||||
import org.openmetadata.catalog.entity.data.Dashboard;
|
||||
import org.openmetadata.catalog.entity.services.DashboardService;
|
||||
import org.openmetadata.catalog.entity.teams.Team;
|
||||
import org.openmetadata.catalog.entity.teams.User;
|
||||
import org.openmetadata.catalog.exception.CatalogExceptionMessage;
|
||||
import org.openmetadata.catalog.resources.dashboards.DashboardResource.DashboardList;
|
||||
import org.openmetadata.catalog.resources.services.DashboardServiceResourceTest;
|
||||
import org.openmetadata.catalog.resources.teams.TeamResourceTest;
|
||||
import org.openmetadata.catalog.resources.teams.UserResourceTest;
|
||||
import org.openmetadata.catalog.type.EntityReference;
|
||||
import org.openmetadata.catalog.util.EntityUtil;
|
||||
import org.openmetadata.catalog.util.JsonUtils;
|
||||
import org.openmetadata.catalog.util.TestUtils;
|
||||
import org.openmetadata.common.utils.JsonSchemaUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.json.JsonPatch;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
|
||||
import static javax.ws.rs.core.Response.Status.CONFLICT;
|
||||
import static javax.ws.rs.core.Response.Status.CREATED;
|
||||
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
|
||||
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
|
||||
import static javax.ws.rs.core.Response.Status.OK;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.openmetadata.catalog.exception.CatalogExceptionMessage.entityNotFound;
|
||||
import static org.openmetadata.catalog.exception.CatalogExceptionMessage.readOnlyAttribute;
|
||||
import static org.openmetadata.catalog.util.TestUtils.adminAuthHeaders;
|
||||
import static org.openmetadata.catalog.util.TestUtils.assertEntityPagination;
|
||||
import static org.openmetadata.catalog.util.TestUtils.assertResponse;
|
||||
import static org.openmetadata.catalog.util.TestUtils.authHeaders;
|
||||
|
||||
public class DashboardResourceTest extends CatalogApplicationTest {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DashboardResourceTest.class);
|
||||
public static User USER1;
|
||||
public static EntityReference USER_OWNER1;
|
||||
public static Team TEAM1;
|
||||
public static EntityReference TEAM_OWNER1;
|
||||
public static EntityReference SUPERSET_REFERENCE;
|
||||
public static EntityReference LOOKER_REFERENCE;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup(TestInfo test) throws HttpResponseException {
|
||||
USER1 = UserResourceTest.createUser(UserResourceTest.create(test), authHeaders("test@open-metadata.org"));
|
||||
USER_OWNER1 = new EntityReference().withId(USER1.getId()).withType("user");
|
||||
|
||||
TEAM1 = TeamResourceTest.createTeam(TeamResourceTest.create(test), adminAuthHeaders());
|
||||
TEAM_OWNER1 = new EntityReference().withId(TEAM1.getId()).withType("team");
|
||||
|
||||
CreateDashboardService createService = new CreateDashboardService().withName("superset")
|
||||
.withServiceType(DashboardServiceType.Superset).withDashboardUrl(TestUtils.DASHBOARD_URL);
|
||||
|
||||
DashboardService service = DashboardServiceResourceTest.createService(createService, adminAuthHeaders());
|
||||
SUPERSET_REFERENCE = EntityUtil.getEntityReference(service);
|
||||
|
||||
createService.withName("looker").withServiceType(DashboardServiceType.Looker);
|
||||
service = DashboardServiceResourceTest.createService(createService, adminAuthHeaders());
|
||||
LOOKER_REFERENCE = EntityUtil.getEntityReference(service);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_dashboardWithLongName_400_badRequest(TestInfo test) {
|
||||
// Create dashboard with mandatory name field empty
|
||||
CreateDashboard create = create(test).withName(TestUtils.LONG_ENTITY_NAME);
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
createDashboard(create, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, "[name size must be between 1 and 64]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_DashboardWithoutName_400_badRequest(TestInfo test) {
|
||||
// Create Dashboard with mandatory name field empty
|
||||
CreateDashboard create = create(test).withName("");
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
createDashboard(create, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, "[name size must be between 1 and 64]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_DashboardAlreadyExists_409_conflict(TestInfo test) throws HttpResponseException {
|
||||
CreateDashboard create = create(test);
|
||||
createDashboard(create, adminAuthHeaders());
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
createDashboard(create, adminAuthHeaders()));
|
||||
assertResponse(exception, CONFLICT, CatalogExceptionMessage.ENTITY_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_validDashboards_as_admin_200_OK(TestInfo test) throws HttpResponseException {
|
||||
// Create team with different optional fields
|
||||
CreateDashboard create = create(test);
|
||||
createAndCheckDashboard(create, adminAuthHeaders());
|
||||
|
||||
create.withName(getDashboardName(test, 1)).withDescription("description");
|
||||
createAndCheckDashboard(create, adminAuthHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_DashboardWithUserOwner_200_ok(TestInfo test) throws HttpResponseException {
|
||||
createAndCheckDashboard(create(test).withOwner(USER_OWNER1), adminAuthHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_DashboardWithTeamOwner_200_ok(TestInfo test) throws HttpResponseException {
|
||||
createAndCheckDashboard(create(test).withOwner(TEAM_OWNER1), adminAuthHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_Dashboard_as_non_admin_401(TestInfo test) {
|
||||
CreateDashboard create = create(test);
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
createDashboard(create, authHeaders("test@open-metadata.org")));
|
||||
assertResponse(exception, FORBIDDEN, "Principal: CatalogPrincipal{name='test'} is not admin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_DashboardWithoutRequiredService_4xx(TestInfo test) {
|
||||
CreateDashboard create = create(test).withService(null);
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
createDashboard(create, adminAuthHeaders()));
|
||||
TestUtils.assertResponseContains(exception, BAD_REQUEST, "service must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_DashboardWithInvalidOwnerType_4xx(TestInfo test) {
|
||||
EntityReference owner = new EntityReference().withId(TEAM1.getId()); /* No owner type is set */
|
||||
|
||||
CreateDashboard create = create(test).withOwner(owner);
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
createDashboard(create, adminAuthHeaders()));
|
||||
TestUtils.assertResponseContains(exception, BAD_REQUEST, "type must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_DashboardWithNonExistentOwner_4xx(TestInfo test) {
|
||||
EntityReference owner = new EntityReference().withId(TestUtils.NON_EXISTENT_ENTITY).withType("user");
|
||||
CreateDashboard create = create(test).withOwner(owner);
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
createDashboard(create, adminAuthHeaders()));
|
||||
assertResponse(exception, NOT_FOUND, entityNotFound("User", TestUtils.NON_EXISTENT_ENTITY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post_DashboardWithDifferentService_200_ok(TestInfo test) throws HttpResponseException {
|
||||
EntityReference[] differentServices = {SUPERSET_REFERENCE, LOOKER_REFERENCE};
|
||||
|
||||
// Create Dashboard for each service and test APIs
|
||||
for (EntityReference service : differentServices) {
|
||||
createAndCheckDashboard(create(test).withService(service), adminAuthHeaders());
|
||||
|
||||
// List Dashboards by filtering on service name and ensure right Dashboards are returned in the response
|
||||
DashboardList list = listDashboards("service", service.getName(), adminAuthHeaders());
|
||||
for (Dashboard db : list.getData()) {
|
||||
assertEquals(service.getName(), db.getService().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_DashboardListWithInvalidLimitOffset_4xx() {
|
||||
// Limit must be >= 1 and <= 1000,000
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, ()
|
||||
-> listDashboards(null, null, -1, null, null, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, "[query param limit must be greater than or equal to 1]");
|
||||
|
||||
exception = assertThrows(HttpResponseException.class, ()
|
||||
-> listDashboards(null, null, 0, null, null, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, "[query param limit must be greater than or equal to 1]");
|
||||
|
||||
exception = assertThrows(HttpResponseException.class, ()
|
||||
-> listDashboards(null, null, 1000001, null, null, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, "[query param limit must be less than or equal to 1000000]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_DashboardListWithInvalidPaginationCursors_4xx() {
|
||||
// Passing both before and after cursors is invalid
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, ()
|
||||
-> listDashboards(null, null, 1, "", "", adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, "Only one of before or after query parameter allowed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_DashboardListWithValidLimitOffset_4xx(TestInfo test) throws HttpResponseException {
|
||||
// Create a large number of Dashboards
|
||||
int maxDashboards = 40;
|
||||
for (int i = 0; i < maxDashboards; i++) {
|
||||
createDashboard(create(test, i), adminAuthHeaders());
|
||||
}
|
||||
|
||||
// List all Dashboards
|
||||
DashboardList allDashboards = listDashboards(null, null, 1000000, null,
|
||||
null, adminAuthHeaders());
|
||||
int totalRecords = allDashboards.getData().size();
|
||||
printDashboards(allDashboards);
|
||||
|
||||
// List limit number Dashboards at a time at various offsets and ensure right results are returned
|
||||
for (int limit = 1; limit < maxDashboards; limit++) {
|
||||
String after = null;
|
||||
String before;
|
||||
int pageCount = 0;
|
||||
int indexInAllDashboards = 0;
|
||||
DashboardList forwardPage;
|
||||
DashboardList backwardPage;
|
||||
do { // For each limit (or page size) - forward scroll till the end
|
||||
LOG.info("Limit {} forward scrollCount {} afterCursor {}", limit, pageCount, after);
|
||||
forwardPage = listDashboards(null, null, limit, null, after, adminAuthHeaders());
|
||||
printDashboards(forwardPage);
|
||||
after = forwardPage.getPaging().getAfter();
|
||||
before = forwardPage.getPaging().getBefore();
|
||||
assertEntityPagination(allDashboards.getData(), forwardPage, limit, indexInAllDashboards);
|
||||
|
||||
if (pageCount == 0) { // CASE 0 - First page is being returned. There is no before cursor
|
||||
assertNull(before);
|
||||
} else {
|
||||
// Make sure scrolling back based on before cursor returns the correct result
|
||||
backwardPage = listDashboards(null, null, limit, before, null, adminAuthHeaders());
|
||||
assertEntityPagination(allDashboards.getData(), backwardPage, limit, (indexInAllDashboards - limit));
|
||||
}
|
||||
|
||||
indexInAllDashboards += forwardPage.getData().size();
|
||||
pageCount++;
|
||||
} while (after != null);
|
||||
|
||||
// We have now reached the last page - test backward scroll till the beginning
|
||||
pageCount = 0;
|
||||
indexInAllDashboards = totalRecords - limit - forwardPage.getData().size();
|
||||
do {
|
||||
LOG.info("Limit {} backward scrollCount {} beforeCursor {}", limit, pageCount, before);
|
||||
forwardPage = listDashboards(null, null, limit, before, null, adminAuthHeaders());
|
||||
printDashboards(forwardPage);
|
||||
before = forwardPage.getPaging().getBefore();
|
||||
assertEntityPagination(allDashboards.getData(), forwardPage, limit, indexInAllDashboards);
|
||||
pageCount++;
|
||||
indexInAllDashboards -= forwardPage.getData().size();
|
||||
} while (before != null);
|
||||
}
|
||||
}
|
||||
|
||||
private void printDashboards(DashboardList list) {
|
||||
list.getData().forEach(Dashboard -> LOG.info("DB {}", Dashboard.getFullyQualifiedName()));
|
||||
LOG.info("before {} after {} ", list.getPaging().getBefore(), list.getPaging().getAfter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put_DashboardUpdateWithNoChange_200(TestInfo test) throws HttpResponseException {
|
||||
// Create a Dashboard with POST
|
||||
CreateDashboard request = create(test).withService(SUPERSET_REFERENCE).withOwner(USER_OWNER1);
|
||||
createAndCheckDashboard(request, adminAuthHeaders());
|
||||
|
||||
// Update Dashboard two times successfully with PUT requests
|
||||
updateAndCheckDashboard(request, OK, adminAuthHeaders());
|
||||
updateAndCheckDashboard(request, OK, adminAuthHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put_DashboardCreate_200(TestInfo test) throws HttpResponseException {
|
||||
// Create a new Dashboard with put
|
||||
CreateDashboard request = create(test).withService(SUPERSET_REFERENCE).withOwner(USER_OWNER1);
|
||||
updateAndCheckDashboard(request.withName(test.getDisplayName()).withDescription(null), CREATED, adminAuthHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put_DashboardCreate_as_owner_200(TestInfo test) throws HttpResponseException {
|
||||
// Create a new Dashboard with put
|
||||
CreateDashboard request = create(test).withService(SUPERSET_REFERENCE).withOwner(USER_OWNER1);
|
||||
// Add Owner as admin
|
||||
createAndCheckDashboard(request, adminAuthHeaders());
|
||||
//Update the table as Owner
|
||||
updateAndCheckDashboard(request.withName(test.getDisplayName()).withDescription(null),
|
||||
CREATED, authHeaders(USER1.getEmail()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put_DashboardNullDescriptionUpdate_200(TestInfo test) throws HttpResponseException {
|
||||
CreateDashboard request = create(test).withService(SUPERSET_REFERENCE).withDescription(null);
|
||||
createAndCheckDashboard(request, adminAuthHeaders());
|
||||
|
||||
// Update null description with a new description
|
||||
Dashboard db = updateAndCheckDashboard(request.withDescription("newDescription"), OK, adminAuthHeaders());
|
||||
assertEquals("newDescription", db.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put_DashboardEmptyDescriptionUpdate_200(TestInfo test) throws HttpResponseException {
|
||||
// Create table with empty description
|
||||
CreateDashboard request = create(test).withService(SUPERSET_REFERENCE).withDescription("");
|
||||
createAndCheckDashboard(request, adminAuthHeaders());
|
||||
|
||||
// Update empty description with a new description
|
||||
Dashboard db = updateAndCheckDashboard(request.withDescription("newDescription"), OK, adminAuthHeaders());
|
||||
assertEquals("newDescription", db.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put_DashboardNonEmptyDescriptionUpdate_200(TestInfo test) throws HttpResponseException {
|
||||
CreateDashboard request = create(test).withService(SUPERSET_REFERENCE).withDescription("description");
|
||||
createAndCheckDashboard(request, adminAuthHeaders());
|
||||
|
||||
// Updating description is ignored when backend already has description
|
||||
Dashboard db = updateDashboard(request.withDescription("newDescription"), OK, adminAuthHeaders());
|
||||
assertEquals("description", db.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put_DashboardUpdateOwner_200(TestInfo test) throws HttpResponseException {
|
||||
CreateDashboard request = create(test).withService(SUPERSET_REFERENCE).withDescription("");
|
||||
createAndCheckDashboard(request, adminAuthHeaders());
|
||||
|
||||
// Change ownership from USER_OWNER1 to TEAM_OWNER1
|
||||
updateAndCheckDashboard(request.withOwner(TEAM_OWNER1), OK, adminAuthHeaders());
|
||||
|
||||
// Remove ownership
|
||||
Dashboard db = updateAndCheckDashboard(request.withOwner(null), OK, adminAuthHeaders());
|
||||
assertNull(db.getOwner());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_nonExistentDashboard_404_notFound() {
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
getDashboard(TestUtils.NON_EXISTENT_ENTITY, adminAuthHeaders()));
|
||||
assertResponse(exception, NOT_FOUND,
|
||||
entityNotFound(Entity.DASHBOARD, TestUtils.NON_EXISTENT_ENTITY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_DashboardWithDifferentFields_200_OK(TestInfo test) throws HttpResponseException {
|
||||
CreateDashboard create = create(test).withDescription("description").withOwner(USER_OWNER1)
|
||||
.withService(SUPERSET_REFERENCE);
|
||||
Dashboard Dashboard = createAndCheckDashboard(create, adminAuthHeaders());
|
||||
validateGetWithDifferentFields(Dashboard, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_DashboardByNameWithDifferentFields_200_OK(TestInfo test) throws HttpResponseException {
|
||||
CreateDashboard create = create(test).withDescription("description").withOwner(USER_OWNER1)
|
||||
.withService(SUPERSET_REFERENCE);
|
||||
Dashboard Dashboard = createAndCheckDashboard(create, adminAuthHeaders());
|
||||
validateGetWithDifferentFields(Dashboard, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patch_DashboardAttributes_200_ok(TestInfo test) throws HttpResponseException, JsonProcessingException {
|
||||
// Create Dashboard without description, owner
|
||||
Dashboard Dashboard = createDashboard(create(test), adminAuthHeaders());
|
||||
assertNull(Dashboard.getDescription());
|
||||
assertNull(Dashboard.getOwner());
|
||||
assertNotNull(Dashboard.getService());
|
||||
|
||||
Dashboard = getDashboard(Dashboard.getId(), "service,owner,usageSummary", adminAuthHeaders());
|
||||
Dashboard.getService().setHref(null); // href is readonly and not patchable
|
||||
|
||||
// Add description, owner when previously they were null
|
||||
Dashboard = patchDashboardAttributesAndCheck(Dashboard, "description", TEAM_OWNER1, adminAuthHeaders());
|
||||
Dashboard.setOwner(TEAM_OWNER1); // Get rid of href and name returned in the response for owner
|
||||
Dashboard.setService(SUPERSET_REFERENCE); // Get rid of href and name returned in the response for service
|
||||
|
||||
// Replace description, tier, owner
|
||||
Dashboard = patchDashboardAttributesAndCheck(Dashboard, "description1", USER_OWNER1, adminAuthHeaders());
|
||||
Dashboard.setOwner(USER_OWNER1); // Get rid of href and name returned in the response for owner
|
||||
Dashboard.setService(SUPERSET_REFERENCE); // Get rid of href and name returned in the response for service
|
||||
|
||||
// Remove description, tier, owner
|
||||
patchDashboardAttributesAndCheck(Dashboard, null, null, adminAuthHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patch_DashboardIDChange_400(TestInfo test) throws HttpResponseException, JsonProcessingException {
|
||||
// Ensure Dashboard ID can't be changed using patch
|
||||
Dashboard Dashboard = createDashboard(create(test), adminAuthHeaders());
|
||||
UUID DashboardId = Dashboard.getId();
|
||||
String DashboardJson = JsonUtils.pojoToJson(Dashboard);
|
||||
Dashboard.setId(UUID.randomUUID());
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
patchDashboard(DashboardId, DashboardJson, Dashboard, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, readOnlyAttribute(Entity.DASHBOARD, "id"));
|
||||
|
||||
// ID can't be deleted
|
||||
Dashboard.setId(null);
|
||||
exception = assertThrows(HttpResponseException.class, () ->
|
||||
patchDashboard(DashboardId, DashboardJson, Dashboard, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, readOnlyAttribute(Entity.DASHBOARD, "id"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patch_DashboardNameChange_400(TestInfo test) throws HttpResponseException, JsonProcessingException {
|
||||
// Ensure Dashboard name can't be changed using patch
|
||||
Dashboard Dashboard = createDashboard(create(test), adminAuthHeaders());
|
||||
String DashboardJson = JsonUtils.pojoToJson(Dashboard);
|
||||
Dashboard.setName("newName");
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
patchDashboard(DashboardJson, Dashboard, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, readOnlyAttribute(Entity.DASHBOARD, "name"));
|
||||
|
||||
// Name can't be removed
|
||||
Dashboard.setName(null);
|
||||
exception = assertThrows(HttpResponseException.class, () ->
|
||||
patchDashboard(DashboardJson, Dashboard, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, readOnlyAttribute(Entity.DASHBOARD, "name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patch_DashboardRemoveService_400(TestInfo test) throws HttpResponseException, JsonProcessingException {
|
||||
// Ensure service corresponding to Dashboard can't be changed by patch operation
|
||||
Dashboard Dashboard = createDashboard(create(test), adminAuthHeaders());
|
||||
Dashboard.getService().setHref(null); // Remove href from returned response as it is read-only field
|
||||
|
||||
String DashboardJson = JsonUtils.pojoToJson(Dashboard);
|
||||
Dashboard.setService(LOOKER_REFERENCE);
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
patchDashboard(DashboardJson, Dashboard, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, readOnlyAttribute(Entity.DASHBOARD, "service"));
|
||||
|
||||
// Service relationship can't be removed
|
||||
Dashboard.setService(null);
|
||||
exception = assertThrows(HttpResponseException.class, () ->
|
||||
patchDashboard(DashboardJson, Dashboard, adminAuthHeaders()));
|
||||
assertResponse(exception, BAD_REQUEST, readOnlyAttribute(Entity.DASHBOARD, "service"));
|
||||
}
|
||||
|
||||
// TODO listing tables test:1
|
||||
// TODO Change service?
|
||||
|
||||
@Test
|
||||
public void delete_emptyDashboard_200_ok(TestInfo test) throws HttpResponseException {
|
||||
Dashboard Dashboard = createDashboard(create(test), adminAuthHeaders());
|
||||
deleteDashboard(Dashboard.getId(), adminAuthHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete_nonEmptyDashboard_4xx() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete_nonExistentDashboard_404() {
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () ->
|
||||
deleteDashboard(TestUtils.NON_EXISTENT_ENTITY, adminAuthHeaders()));
|
||||
assertResponse(exception, NOT_FOUND, entityNotFound(Entity.DASHBOARD, TestUtils.NON_EXISTENT_ENTITY));
|
||||
}
|
||||
|
||||
public static Dashboard createAndCheckDashboard(CreateDashboard create,
|
||||
Map<String, String> authHeaders) throws HttpResponseException {
|
||||
Dashboard Dashboard = createDashboard(create, authHeaders);
|
||||
validateDashboard(Dashboard, create.getDescription(), create.getOwner(), create.getService());
|
||||
return getAndValidate(Dashboard.getId(), create, authHeaders);
|
||||
}
|
||||
|
||||
public static Dashboard updateAndCheckDashboard(CreateDashboard create,
|
||||
Status status,
|
||||
Map<String, String> authHeaders) throws HttpResponseException {
|
||||
Dashboard updatedDashboard = updateDashboard(create, status, authHeaders);
|
||||
validateDashboard(updatedDashboard, create.getDescription(), create.getOwner(), create.getService());
|
||||
|
||||
// GET the newly updated Dashboard and validate
|
||||
return getAndValidate(updatedDashboard.getId(), create, authHeaders);
|
||||
}
|
||||
|
||||
// Make sure in GET operations the returned Dashboard has all the required information passed during creation
|
||||
public static Dashboard getAndValidate(UUID DashboardId,
|
||||
CreateDashboard create,
|
||||
Map<String, String> authHeaders) throws HttpResponseException {
|
||||
// GET the newly created Dashboard by ID and validate
|
||||
Dashboard Dashboard = getDashboard(DashboardId, "service,owner", authHeaders);
|
||||
validateDashboard(Dashboard, create.getDescription(), create.getOwner(), create.getService());
|
||||
|
||||
// GET the newly created Dashboard by name and validate
|
||||
String fqn = Dashboard.getFullyQualifiedName();
|
||||
Dashboard = getDashboardByName(fqn, "service,owner", authHeaders);
|
||||
return validateDashboard(Dashboard, create.getDescription(), create.getOwner(), create.getService());
|
||||
}
|
||||
|
||||
public static Dashboard updateDashboard(CreateDashboard create,
|
||||
Status status,
|
||||
Map<String, String> authHeaders) throws HttpResponseException {
|
||||
return TestUtils.put(getResource("dashboards"),
|
||||
create, Dashboard.class, status, authHeaders);
|
||||
}
|
||||
|
||||
public static Dashboard createDashboard(CreateDashboard create,
|
||||
Map<String, String> authHeaders) throws HttpResponseException {
|
||||
return TestUtils.post(getResource("dashboards"), create, Dashboard.class, authHeaders);
|
||||
}
|
||||
|
||||
/** Validate returned fields GET .../dashboards/{id}?fields="..." or GET .../dashboards/name/{fqn}?fields="..." */
|
||||
private void validateGetWithDifferentFields(Dashboard Dashboard, boolean byName) throws HttpResponseException {
|
||||
// .../Dashboards?fields=owner
|
||||
String fields = "owner";
|
||||
Dashboard = byName ? getDashboardByName(Dashboard.getFullyQualifiedName(), fields, adminAuthHeaders()) :
|
||||
getDashboard(Dashboard.getId(), fields, adminAuthHeaders());
|
||||
assertNotNull(Dashboard.getOwner());
|
||||
assertNull(Dashboard.getService());
|
||||
assertNull(Dashboard.getCharts());
|
||||
|
||||
// .../Dashboards?fields=owner,service
|
||||
fields = "owner,service";
|
||||
Dashboard = byName ? getDashboardByName(Dashboard.getFullyQualifiedName(), fields, adminAuthHeaders()) :
|
||||
getDashboard(Dashboard.getId(), fields, adminAuthHeaders());
|
||||
assertNotNull(Dashboard.getOwner());
|
||||
assertNotNull(Dashboard.getService());
|
||||
assertNull(Dashboard.getCharts());
|
||||
|
||||
// .../Dashboards?fields=owner,service,tables
|
||||
fields = "owner,service,charts,usageSummary";
|
||||
Dashboard = byName ? getDashboardByName(Dashboard.getFullyQualifiedName(), fields, adminAuthHeaders()) :
|
||||
getDashboard(Dashboard.getId(), fields, adminAuthHeaders());
|
||||
assertNotNull(Dashboard.getOwner());
|
||||
assertNotNull(Dashboard.getService());
|
||||
assertNotNull(Dashboard.getCharts());
|
||||
TestUtils.validateEntityReference(Dashboard.getCharts());
|
||||
assertNotNull(Dashboard.getUsageSummary());
|
||||
|
||||
}
|
||||
|
||||
private static Dashboard validateDashboard(Dashboard dashboard, String expectedDescription,
|
||||
EntityReference expectedOwner, EntityReference expectedService) {
|
||||
assertNotNull(dashboard.getId());
|
||||
assertNotNull(dashboard.getHref());
|
||||
assertEquals(expectedDescription, dashboard.getDescription());
|
||||
|
||||
// Validate owner
|
||||
if (expectedOwner != null) {
|
||||
TestUtils.validateEntityReference(dashboard.getOwner());
|
||||
assertEquals(expectedOwner.getId(), dashboard.getOwner().getId());
|
||||
assertEquals(expectedOwner.getType(), dashboard.getOwner().getType());
|
||||
assertNotNull(dashboard.getOwner().getHref());
|
||||
}
|
||||
|
||||
// Validate service
|
||||
if (expectedService != null) {
|
||||
TestUtils.validateEntityReference(dashboard.getService());
|
||||
assertEquals(expectedService.getId(), dashboard.getService().getId());
|
||||
assertEquals(expectedService.getType(), dashboard.getService().getType());
|
||||
}
|
||||
return dashboard;
|
||||
}
|
||||
|
||||
private Dashboard patchDashboardAttributesAndCheck(Dashboard dashboard, String newDescription,
|
||||
EntityReference newOwner, Map<String, String> authHeaders)
|
||||
throws JsonProcessingException, HttpResponseException {
|
||||
String DashboardJson = JsonUtils.pojoToJson(dashboard);
|
||||
|
||||
// Update the table attributes
|
||||
dashboard.setDescription(newDescription);
|
||||
dashboard.setOwner(newOwner);
|
||||
|
||||
// Validate information returned in patch response has the updates
|
||||
Dashboard updatedDashboard = patchDashboard(DashboardJson, dashboard, authHeaders);
|
||||
validateDashboard(updatedDashboard, dashboard.getDescription(), newOwner, null);
|
||||
|
||||
// GET the table and Validate information returned
|
||||
Dashboard getDashboard = getDashboard(dashboard.getId(), "service,owner", authHeaders);
|
||||
validateDashboard(getDashboard, dashboard.getDescription(), newOwner, null);
|
||||
return updatedDashboard;
|
||||
}
|
||||
|
||||
private Dashboard patchDashboard(UUID dashboardId, String originalJson, Dashboard updatedDashboard,
|
||||
Map<String, String> authHeaders)
|
||||
throws JsonProcessingException, HttpResponseException {
|
||||
String updateDashboardJson = JsonUtils.pojoToJson(updatedDashboard);
|
||||
JsonPatch patch = JsonSchemaUtil.getJsonPatch(originalJson, updateDashboardJson);
|
||||
return TestUtils.patch(getResource("dashboards/" + dashboardId), patch, Dashboard.class, authHeaders);
|
||||
}
|
||||
|
||||
private Dashboard patchDashboard(String originalJson,
|
||||
Dashboard updatedDashboard,
|
||||
Map<String, String> authHeaders)
|
||||
throws JsonProcessingException, HttpResponseException {
|
||||
return patchDashboard(updatedDashboard.getId(), originalJson, updatedDashboard, authHeaders);
|
||||
}
|
||||
|
||||
public static void getDashboard(UUID id, Map<String, String> authHeaders) throws HttpResponseException {
|
||||
getDashboard(id, null, authHeaders);
|
||||
}
|
||||
|
||||
public static Dashboard getDashboard(UUID id, String fields, Map<String, String> authHeaders)
|
||||
throws HttpResponseException {
|
||||
WebTarget target = getResource("dashboards/" + id);
|
||||
target = fields != null ? target.queryParam("fields", fields): target;
|
||||
return TestUtils.get(target, Dashboard.class, authHeaders);
|
||||
}
|
||||
|
||||
public static Dashboard getDashboardByName(String fqn, String fields, Map<String, String> authHeaders)
|
||||
throws HttpResponseException {
|
||||
WebTarget target = getResource("dashboards/name/" + fqn);
|
||||
target = fields != null ? target.queryParam("fields", fields): target;
|
||||
return TestUtils.get(target, Dashboard.class, authHeaders);
|
||||
}
|
||||
|
||||
public static DashboardList listDashboards(String fields, String serviceParam, Map<String, String> authHeaders)
|
||||
throws HttpResponseException {
|
||||
return listDashboards(fields, serviceParam, null, null, null, authHeaders);
|
||||
}
|
||||
|
||||
public static DashboardList listDashboards(String fields, String serviceParam, Integer limitParam,
|
||||
String before, String after, Map<String, String> authHeaders)
|
||||
throws HttpResponseException {
|
||||
WebTarget target = getResource("dashboards");
|
||||
target = fields != null ? target.queryParam("fields", fields): target;
|
||||
target = serviceParam != null ? target.queryParam("service", serviceParam): target;
|
||||
target = limitParam != null ? target.queryParam("limit", limitParam): target;
|
||||
target = before != null ? target.queryParam("before", before) : target;
|
||||
target = after != null ? target.queryParam("after", after) : target;
|
||||
return TestUtils.get(target, DashboardList.class, authHeaders);
|
||||
}
|
||||
|
||||
private void deleteDashboard(UUID id, Map<String, String> authHeaders) throws HttpResponseException {
|
||||
TestUtils.delete(getResource("dashboards/" + id), authHeaders);
|
||||
|
||||
// Ensure deleted Dashboard does not exist
|
||||
HttpResponseException exception = assertThrows(HttpResponseException.class, () -> getDashboard(id, authHeaders));
|
||||
assertResponse(exception, NOT_FOUND, entityNotFound(Entity.DASHBOARD, id));
|
||||
}
|
||||
|
||||
public static String getDashboardName(TestInfo test) {
|
||||
return String.format("dash_%s", test.getDisplayName());
|
||||
}
|
||||
|
||||
public static String getDashboardName(TestInfo test, int index) {
|
||||
return String.format("dash%d_%s", index, test.getDisplayName());
|
||||
}
|
||||
|
||||
public static CreateDashboard create(TestInfo test) {
|
||||
return new CreateDashboard().withName(getDashboardName(test)).withService(SUPERSET_REFERENCE);
|
||||
}
|
||||
|
||||
public static CreateDashboard create(TestInfo test, int index) {
|
||||
return new CreateDashboard().withName(getDashboardName(test, index)).withService(SUPERSET_REFERENCE);
|
||||
}
|
||||
}
|
||||
@ -49,6 +49,8 @@ public final class TestUtils {
|
||||
public static final String LONG_ENTITY_NAME = "012345678901234567890123456789012345678901234567890123456789012345";
|
||||
public static final UUID NON_EXISTENT_ENTITY = UUID.randomUUID();
|
||||
public static JdbcInfo JDBC_INFO;
|
||||
public static URI DASHBOARD_URL;
|
||||
|
||||
static {
|
||||
try {
|
||||
JDBC_INFO = new JdbcInfo().withConnectionUrl(new URI("jdbc:service://")).withDriverClass("driverClass");
|
||||
@ -58,6 +60,15 @@ public final class TestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
DASHBOARD_URL = new URI("http://localhost:8088");
|
||||
} catch (URISyntaxException e) {
|
||||
DASHBOARD_URL = null;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private TestUtils() {
|
||||
}
|
||||
|
||||
@ -169,7 +180,8 @@ public final class TestUtils {
|
||||
assertNotNull(ref.getName());
|
||||
assertNotNull(ref.getType());
|
||||
// Ensure data entities use fully qualified name
|
||||
if (List.of("table", "database", "metrics", "dashboard", "pipeline", "report", "topic").contains(ref.getName())) {
|
||||
if (List.of("table", "database", "metrics", "dashboard", "pipeline", "report", "topic", "chart")
|
||||
.contains(ref.getName())) {
|
||||
ref.getName().contains("."); // FullyQualifiedName has "." as separator
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user