mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-29 09:42:23 +00:00 
			
		
		
		
	Add Chart and Dashboard Service entities
This commit is contained in:
		
							parent
							
								
									8f5b3c934d
								
							
						
					
					
						commit
						0e4a3b26f9
					
				| @ -78,8 +78,6 @@ public abstract class ChartRepository { | |||||||
|   @CreateSqlObject |   @CreateSqlObject | ||||||
|   abstract TagRepository.TagDAO tagDAO(); |   abstract TagRepository.TagDAO tagDAO(); | ||||||
| 
 | 
 | ||||||
|   @CreateSqlObject |  | ||||||
|   abstract UsageDAO usageDAO(); |  | ||||||
| 
 | 
 | ||||||
|   @Transaction |   @Transaction | ||||||
|   public List<Chart> listAfter(Fields fields, String serviceName, int limitParam, String after) throws IOException, |   public List<Chart> listAfter(Fields fields, String serviceName, int limitParam, String after) throws IOException, | ||||||
|  | |||||||
| @ -16,36 +16,59 @@ | |||||||
| 
 | 
 | ||||||
| package org.openmetadata.catalog.jdbi3; | 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.exception.CatalogExceptionMessage; | ||||||
|  | import org.openmetadata.catalog.exception.EntityNotFoundException; | ||||||
| import org.openmetadata.catalog.jdbi3.TeamRepository.TeamDAO; | import org.openmetadata.catalog.jdbi3.TeamRepository.TeamDAO; | ||||||
| import org.openmetadata.catalog.jdbi3.UserRepository.UserDAO; | 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.dashboards.DashboardResource; | ||||||
| import org.openmetadata.catalog.Entity; | import org.openmetadata.catalog.Entity; | ||||||
| import org.openmetadata.catalog.entity.data.Dashboard; | import org.openmetadata.catalog.entity.data.Dashboard; | ||||||
| import org.openmetadata.catalog.jdbi3.UsageRepository.UsageDAO; | import org.openmetadata.catalog.jdbi3.UsageRepository.UsageDAO; | ||||||
|  | import org.openmetadata.catalog.jdbi3.ChartRepository.ChartDAO; | ||||||
|  | import org.openmetadata.catalog.jdbi3.DashboardServiceRepository.DashboardServiceDAO; | ||||||
| import org.openmetadata.catalog.type.EntityReference; | import org.openmetadata.catalog.type.EntityReference; | ||||||
|  | import org.openmetadata.catalog.type.TagLabel; | ||||||
| import org.openmetadata.catalog.util.EntityUtil; | import org.openmetadata.catalog.util.EntityUtil; | ||||||
| import org.openmetadata.catalog.util.EntityUtil.Fields; | import org.openmetadata.catalog.util.EntityUtil.Fields; | ||||||
| import org.openmetadata.catalog.util.JsonUtils; | import org.openmetadata.catalog.util.JsonUtils; | ||||||
| import org.openmetadata.catalog.util.RestUtil.PutResponse; | import org.openmetadata.catalog.util.RestUtil.PutResponse; | ||||||
|  | import org.openmetadata.common.utils.CipherText; | ||||||
| import org.skife.jdbi.v2.sqlobject.Bind; | import org.skife.jdbi.v2.sqlobject.Bind; | ||||||
| import org.skife.jdbi.v2.sqlobject.CreateSqlObject; | import org.skife.jdbi.v2.sqlobject.CreateSqlObject; | ||||||
| import org.skife.jdbi.v2.sqlobject.SqlQuery; | import org.skife.jdbi.v2.sqlobject.SqlQuery; | ||||||
| import org.skife.jdbi.v2.sqlobject.SqlUpdate; | import org.skife.jdbi.v2.sqlobject.SqlUpdate; | ||||||
| import org.skife.jdbi.v2.sqlobject.Transaction; | import org.skife.jdbi.v2.sqlobject.Transaction; | ||||||
| 
 | 
 | ||||||
|  | import javax.json.JsonPatch; | ||||||
| import javax.ws.rs.core.Response; | import javax.ws.rs.core.Response; | ||||||
| import javax.ws.rs.core.Response.Status; | import javax.ws.rs.core.Response.Status; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.security.GeneralSecurityException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
|  | import static org.openmetadata.catalog.exception.CatalogExceptionMessage.entityNotFound; | ||||||
|  | 
 | ||||||
| public abstract class DashboardRepository { | public abstract class DashboardRepository { | ||||||
|   private static final Fields METRICS_UPDATE_FIELDS = new Fields(DashboardResource.FIELD_LIST, |   private static final Fields DASHBOARD_UPDATE_FIELDS = new Fields(DashboardResource.FIELD_LIST, | ||||||
|           "owner,service"); |           "owner,service,tags,charts"); | ||||||
|  |   private static final Fields DASHBOARD_PATCH_FIELDS = new Fields(DashboardResource.FIELD_LIST, | ||||||
|  |           "owner,service,tags,charts"); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|   @CreateSqlObject |   @CreateSqlObject | ||||||
|   abstract DashboardDAO dashboardDAO(); |   abstract DashboardDAO dashboardDAO(); | ||||||
| 
 | 
 | ||||||
|  |   @CreateSqlObject | ||||||
|  |   abstract ChartDAO chartDAO(); | ||||||
|  | 
 | ||||||
|  |   @CreateSqlObject | ||||||
|  |   abstract DashboardServiceDAO dashboardServiceDAO(); | ||||||
|  | 
 | ||||||
|   @CreateSqlObject |   @CreateSqlObject | ||||||
|   abstract EntityRelationshipDAO relationshipDAO(); |   abstract EntityRelationshipDAO relationshipDAO(); | ||||||
| 
 | 
 | ||||||
| @ -58,6 +81,35 @@ public abstract class DashboardRepository { | |||||||
|   @CreateSqlObject |   @CreateSqlObject | ||||||
|   abstract UsageDAO usageDAO(); |   abstract UsageDAO usageDAO(); | ||||||
| 
 | 
 | ||||||
|  |   @CreateSqlObject | ||||||
|  |   abstract TagRepository.TagDAO tagDAO(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   @Transaction | ||||||
|  |   public List<Dashboard> listAfter(Fields fields, String serviceName, int limitParam, String after) throws IOException, | ||||||
|  |           GeneralSecurityException { | ||||||
|  |     // forward scrolling, either because after != null or first page is being asked | ||||||
|  |     List<String> jsons = dashboardDAO().listAfter(serviceName, limitParam, after == null ? "" : | ||||||
|  |             CipherText.instance().decrypt(after)); | ||||||
|  | 
 | ||||||
|  |     List<Dashboard> dashboards = new ArrayList<>(); | ||||||
|  |     for (String json : jsons) { | ||||||
|  |       dashboards.add(setFields(JsonUtils.readValue(json, Dashboard.class), fields)); | ||||||
|  |     } | ||||||
|  |     return dashboards; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Transaction | ||||||
|  |   public List<Dashboard> listBefore(Fields fields, String serviceName, int limitParam, String before) | ||||||
|  |           throws IOException, GeneralSecurityException { | ||||||
|  |     // Reverse scrolling | ||||||
|  |     List<String> jsons = dashboardDAO().listBefore(serviceName, limitParam, CipherText.instance().decrypt(before)); | ||||||
|  |     List<Dashboard> dashboards = new ArrayList<>(); | ||||||
|  |     for (String json : jsons) { | ||||||
|  |       dashboards.add(setFields(JsonUtils.readValue(json, Dashboard.class), fields)); | ||||||
|  |     } | ||||||
|  |     return dashboards; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   @Transaction |   @Transaction | ||||||
|   public Dashboard create(Dashboard dashboard, EntityReference service, EntityReference owner) throws IOException { |   public Dashboard create(Dashboard dashboard, EntityReference service, EntityReference owner) throws IOException { | ||||||
| @ -81,8 +133,8 @@ public abstract class DashboardRepository { | |||||||
|     dashboardDAO().update(storedDashboard.getId().toString(), JsonUtils.pojoToJson(storedDashboard)); |     dashboardDAO().update(storedDashboard.getId().toString(), JsonUtils.pojoToJson(storedDashboard)); | ||||||
| 
 | 
 | ||||||
|     // Update owner relationship |     // Update owner relationship | ||||||
|     setFields(storedDashboard, METRICS_UPDATE_FIELDS); // First get the ownership information |     setFields(storedDashboard, DASHBOARD_UPDATE_FIELDS); // First get the ownership information | ||||||
|     updateOwner(storedDashboard, storedDashboard.getOwner(), newOwner); |     updateRelationships(storedDashboard, updatedDashboard); | ||||||
| 
 | 
 | ||||||
|     // Service can't be changed in update since service name is part of FQN and |     // 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 |     // change to a different service will result in a different FQN and creation of a new database under the new service | ||||||
| @ -91,27 +143,66 @@ public abstract class DashboardRepository { | |||||||
|     return new PutResponse<>(Response.Status.OK, storedDashboard); |     return new PutResponse<>(Response.Status.OK, storedDashboard); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public Dashboard get(String id, Fields fields) throws IOException { |   @Transaction | ||||||
|     return setFields(EntityUtil.validate(id, dashboardDAO().findById(id), Dashboard.class), fields); |   public Dashboard patch(String id, JsonPatch patch) throws IOException { | ||||||
|  |     Dashboard original = setFields(validateDashboard(id), DASHBOARD_PATCH_FIELDS); | ||||||
|  |     Dashboard updated = JsonUtils.applyPatch(original, patch, Dashboard.class); | ||||||
|  |     patch(original, updated); | ||||||
|  |     return updated; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public List<Dashboard> list(Fields fields) throws IOException { |   @Transaction | ||||||
|     List<String> jsonList = dashboardDAO().list(); |   public Status addFollower(String dashboardId, String userId) throws IOException { | ||||||
|     List<Dashboard> dashboardList = new ArrayList<>(); |     EntityUtil.validate(dashboardId, dashboardDAO().findById(dashboardId), Dashboard.class); | ||||||
|     for (String json : jsonList) { |     return EntityUtil.addFollower(relationshipDAO(), userDAO(), dashboardId, Entity.DASHBOARD, userId, Entity.USER) ? | ||||||
|       dashboardList.add(setFields(JsonUtils.readValue(json, Dashboard.class), fields)); |             Status.CREATED : Status.OK; | ||||||
|   } |   } | ||||||
|     return dashboardList; | 
 | ||||||
|  |   @Transaction | ||||||
|  |   public void deleteFollower(String dashboardId, String userId) { | ||||||
|  |     EntityUtil.validateUser(userDAO(), userId); | ||||||
|  |     EntityUtil.removeFollower(relationshipDAO(), dashboardId, userId); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Transaction | ||||||
|  |   public void delete(String id) { | ||||||
|  |     if (relationshipDAO().findToCount(id, Relationship.CONTAINS.ordinal(), Entity.DASHBOARD) > 0) { | ||||||
|  |       throw new IllegalArgumentException("Dashboard is not empty"); | ||||||
|  |     } | ||||||
|  |     if (dashboardDAO().delete(id) <= 0) { | ||||||
|  |       throw EntityNotFoundException.byMessage(entityNotFound(Entity.DASHBOARD, id)); | ||||||
|  |     } | ||||||
|  |     relationshipDAO().deleteAll(id); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static List<EntityReference> toEntityReference(List<Chart> charts) { | ||||||
|  |     List<EntityReference> refList = new ArrayList<>(); | ||||||
|  |     for (Chart chart: charts) { | ||||||
|  |       refList.add(EntityUtil.getEntityReference(chart)); | ||||||
|  |     } | ||||||
|  |     return refList; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Dashboard get(String id, Fields fields) throws IOException { | ||||||
|  |     return setFields(EntityUtil.validate(id, dashboardDAO().findById(id), Dashboard.class), fields); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private Dashboard setFields(Dashboard dashboard, Fields fields) throws IOException { |   private Dashboard setFields(Dashboard dashboard, Fields fields) throws IOException { | ||||||
|     dashboard.setOwner(fields.contains("owner") ? getOwner(dashboard) : null); |     dashboard.setOwner(fields.contains("owner") ? getOwner(dashboard) : null); | ||||||
|     dashboard.setService(fields.contains("service") ? getService(dashboard) : null); |     dashboard.setService(fields.contains("service") ? getService(dashboard) : null); | ||||||
|  |     dashboard.setFollowers(fields.contains("followers") ? getFollowers(dashboard) : null); | ||||||
|  |     dashboard.setCharts(fields.contains("charts") ? toEntityReference(getCharts(dashboard)) : null); | ||||||
|  |     dashboard.setTags(fields.contains("tags") ? getTags(dashboard.getFullyQualifiedName()) : null); | ||||||
|     dashboard.setUsageSummary(fields.contains("usageSummary") ? EntityUtil.getLatestUsage(usageDAO(), |     dashboard.setUsageSummary(fields.contains("usageSummary") ? EntityUtil.getLatestUsage(usageDAO(), | ||||||
|             dashboard.getId()) : null); |             dashboard.getId()) : null); | ||||||
|     return dashboard; |     return dashboard; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   private List<TagLabel> getTags(String fqn) { | ||||||
|  |     return tagDAO().getTags(fqn); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|   private Dashboard createInternal(Dashboard dashboard, EntityReference service, EntityReference owner) |   private Dashboard createInternal(Dashboard dashboard, EntityReference service, EntityReference owner) | ||||||
|           throws IOException { |           throws IOException { | ||||||
|     String fqn = service.getName() + "." + dashboard.getName(); |     String fqn = service.getName() + "." + dashboard.getName(); | ||||||
| @ -121,7 +212,7 @@ public abstract class DashboardRepository { | |||||||
| 
 | 
 | ||||||
|     dashboardDAO().insert(JsonUtils.pojoToJson(dashboard)); |     dashboardDAO().insert(JsonUtils.pojoToJson(dashboard)); | ||||||
|     setService(dashboard, service); |     setService(dashboard, service); | ||||||
|     setOwner(dashboard, owner); |     addRelationships(dashboard); | ||||||
|     return dashboard; |     return dashboard; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -143,6 +234,32 @@ public abstract class DashboardRepository { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   private void patch(Dashboard original, Dashboard updated) throws IOException { | ||||||
|  |     String dashboardId = original.getId().toString(); | ||||||
|  |     if (!original.getId().equals(updated.getId())) { | ||||||
|  |       throw new IllegalArgumentException(CatalogExceptionMessage.readOnlyAttribute(Entity.DASHBOARD, "id")); | ||||||
|  |     } | ||||||
|  |     if (!original.getName().equals(updated.getName())) { | ||||||
|  |       throw new IllegalArgumentException(CatalogExceptionMessage.readOnlyAttribute(Entity.DASHBOARD, "name")); | ||||||
|  |     } | ||||||
|  |     if (updated.getService() == null || !original.getService().getId().equals(updated.getService().getId())) { | ||||||
|  |       throw new IllegalArgumentException(CatalogExceptionMessage.readOnlyAttribute(Entity.DASHBOARD, | ||||||
|  |               "service")); | ||||||
|  |     } | ||||||
|  |     // Validate new owner | ||||||
|  |     EntityReference newOwner = EntityUtil.populateOwner(userDAO(), teamDAO(), updated.getOwner()); | ||||||
|  | 
 | ||||||
|  |     EntityReference newService = updated.getService(); | ||||||
|  | 
 | ||||||
|  |     updated.setHref(null); | ||||||
|  |     updated.setOwner(null); | ||||||
|  |     updated.setService(null); | ||||||
|  |     dashboardDAO().update(dashboardId, JsonUtils.pojoToJson(updated)); | ||||||
|  |     updateOwner(updated, original.getOwner(), newOwner); | ||||||
|  |     updated.setService(newService); | ||||||
|  |     applyTags(updated); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   private EntityReference getOwner(Dashboard dashboard) throws IOException { |   private EntityReference getOwner(Dashboard dashboard) throws IOException { | ||||||
|     return dashboard == null ? null : EntityUtil.populateOwner(dashboard.getId(), relationshipDAO(), |     return dashboard == null ? null : EntityUtil.populateOwner(dashboard.getId(), relationshipDAO(), | ||||||
|             userDAO(), teamDAO()); |             userDAO(), teamDAO()); | ||||||
| @ -158,6 +275,57 @@ public abstract class DashboardRepository { | |||||||
|     dashboard.setOwner(newOwner); |     dashboard.setOwner(newOwner); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   private void applyTags(Dashboard dashboard) throws IOException { | ||||||
|  |     // Add dashboard level tags by adding tag to dashboard relationship | ||||||
|  |     EntityUtil.applyTags(tagDAO(), dashboard.getTags(), dashboard.getFullyQualifiedName()); | ||||||
|  |     dashboard.setTags(getTags(dashboard.getFullyQualifiedName())); // Update tag to handle additional derived tags | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private List<EntityReference> getFollowers(Dashboard dashboard) throws IOException { | ||||||
|  |     return dashboard == null ? null : EntityUtil.getFollowers(dashboard.getId(), relationshipDAO(), userDAO()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private List<Chart> getCharts(Dashboard dashboard) throws IOException { | ||||||
|  |     if (dashboard == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String dashboardId = dashboard.getId().toString(); | ||||||
|  |     List<String> chartIds = relationshipDAO().findTo(dashboardId, Relationship.CONTAINS.ordinal(), Entity.CHART); | ||||||
|  |     List<Chart> charts = new ArrayList<>(); | ||||||
|  |     for (String chartId : chartIds) { | ||||||
|  |       String json = chartDAO().findById(chartId); | ||||||
|  |       Chart chart = JsonUtils.readValue(json, Chart.class); | ||||||
|  |       charts.add(chart); | ||||||
|  |     } | ||||||
|  |     return charts; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   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()); | ||||||
|  |     } | ||||||
|  |     // Add owner relationship | ||||||
|  |     EntityUtil.setOwner(relationshipDAO(), dashboard.getId(), Entity.DASHBOARD, dashboard.getOwner()); | ||||||
|  | 
 | ||||||
|  |     // Add tag to dashboard relationship | ||||||
|  |     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); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public interface DashboardDAO { |   public interface DashboardDAO { | ||||||
|     @SqlUpdate("INSERT INTO dashboard_entity(json) VALUES (:json)") |     @SqlUpdate("INSERT INTO dashboard_entity(json) VALUES (:json)") | ||||||
|     void insert(@Bind("json") String json); |     void insert(@Bind("json") String json); | ||||||
| @ -171,7 +339,28 @@ public abstract class DashboardRepository { | |||||||
|     @SqlQuery("SELECT json FROM dashboard_entity WHERE fullyQualifiedName = :name") |     @SqlQuery("SELECT json FROM dashboard_entity WHERE fullyQualifiedName = :name") | ||||||
|     String findByFQN(@Bind("name") String name); |     String findByFQN(@Bind("name") String name); | ||||||
| 
 | 
 | ||||||
|     @SqlQuery("SELECT json FROM dashboard_entity") |     @SqlQuery( | ||||||
|     List<String> list(); |             "SELECT json FROM (" + | ||||||
|  |                     "SELECT fullyQualifiedName, json FROM dashboard_entity WHERE " + | ||||||
|  |                     "(fullyQualifiedName LIKE CONCAT(:fqnPrefix, '.%') OR :fqnPrefix IS NULL) AND " +// Filter by | ||||||
|  |                     // service name | ||||||
|  |                     "fullyQualifiedName < :before " + // Pagination by dashboard fullyQualifiedName | ||||||
|  |                     "ORDER BY fullyQualifiedName DESC " + // Pagination ordering by chart fullyQualifiedName | ||||||
|  |                     "LIMIT :limit" + | ||||||
|  |                     ") last_rows_subquery ORDER BY fullyQualifiedName") | ||||||
|  |     List<String> listBefore(@Bind("fqnPrefix") String fqnPrefix, @Bind("limit") int limit, | ||||||
|  |                             @Bind("before") String before); | ||||||
|  | 
 | ||||||
|  |     @SqlQuery("SELECT json FROM chart_entity WHERE " + | ||||||
|  |             "(fullyQualifiedName LIKE CONCAT(:fqnPrefix, '.%') OR :fqnPrefix IS NULL) AND " + | ||||||
|  |             "fullyQualifiedName > :after " + | ||||||
|  |             "ORDER BY fullyQualifiedName " + | ||||||
|  |             "LIMIT :limit") | ||||||
|  |     List<String> listAfter(@Bind("fqnPrefix") String fqnPrefix, @Bind("limit") int limit, | ||||||
|  |                            @Bind("after") String after); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     @SqlUpdate("DELETE FROM dashboard_entity WHERE id = :id") | ||||||
|  |     int delete(@Bind("id") String id); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -80,7 +80,7 @@ public abstract class TableRepository { | |||||||
|           "owner,columns,database,tags,tableConstraints"); |           "owner,columns,database,tags,tableConstraints"); | ||||||
|   // Table fields that can be updated in a PUT request |   // Table fields that can be updated in a PUT request | ||||||
|   private static final Fields TABLE_UPDATE_FIELDS = new Fields(TableResource.FIELD_LIST, |   private static final Fields TABLE_UPDATE_FIELDS = new Fields(TableResource.FIELD_LIST, | ||||||
|           "owner,columns,database,tags, tableConstraints"); |           "owner,columns,database,tags,tableConstraints"); | ||||||
| 
 | 
 | ||||||
|   @CreateSqlObject |   @CreateSqlObject | ||||||
|   abstract DatabaseDAO databaseDAO(); |   abstract DatabaseDAO databaseDAO(); | ||||||
|  | |||||||
| @ -27,12 +27,9 @@ import io.swagger.v3.oas.annotations.media.Schema; | |||||||
| import io.swagger.v3.oas.annotations.parameters.RequestBody; | import io.swagger.v3.oas.annotations.parameters.RequestBody; | ||||||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||||||
| import org.openmetadata.catalog.api.data.CreateChart; | import org.openmetadata.catalog.api.data.CreateChart; | ||||||
| import org.openmetadata.catalog.api.data.CreateTopic; |  | ||||||
| import org.openmetadata.catalog.entity.data.Chart; | import org.openmetadata.catalog.entity.data.Chart; | ||||||
| import org.openmetadata.catalog.entity.data.Dashboard; | import org.openmetadata.catalog.entity.data.Dashboard; | ||||||
| import org.openmetadata.catalog.entity.data.Topic; |  | ||||||
| import org.openmetadata.catalog.jdbi3.ChartRepository; | import org.openmetadata.catalog.jdbi3.ChartRepository; | ||||||
| import org.openmetadata.catalog.jdbi3.TopicRepository; |  | ||||||
| import org.openmetadata.catalog.resources.Collection; | import org.openmetadata.catalog.resources.Collection; | ||||||
| import org.openmetadata.catalog.security.CatalogAuthorizer; | import org.openmetadata.catalog.security.CatalogAuthorizer; | ||||||
| import org.openmetadata.catalog.security.SecurityUtil; | import org.openmetadata.catalog.security.SecurityUtil; | ||||||
| @ -80,7 +77,7 @@ import java.util.UUID; | |||||||
| @Api(value = "Chart data asset collection", tags = "Chart data asset collection") | @Api(value = "Chart data asset collection", tags = "Chart data asset collection") | ||||||
| @Produces(MediaType.APPLICATION_JSON) | @Produces(MediaType.APPLICATION_JSON) | ||||||
| @Consumes(MediaType.APPLICATION_JSON) | @Consumes(MediaType.APPLICATION_JSON) | ||||||
| @Collection(name = "topics", repositoryClass = "org.openmetadata.catalog.jdbi3.ChartRepository") | @Collection(name = "charts", repositoryClass = "org.openmetadata.catalog.jdbi3.ChartRepository") | ||||||
| public class ChartResource { | public class ChartResource { | ||||||
|   private static final Logger LOG = LoggerFactory.getLogger(ChartResource.class); |   private static final Logger LOG = LoggerFactory.getLogger(ChartResource.class); | ||||||
|   private static final String CHART_COLLECTION_PATH = "v1/charts/"; |   private static final String CHART_COLLECTION_PATH = "v1/charts/"; | ||||||
| @ -146,15 +143,15 @@ public class ChartResource { | |||||||
|                         @Parameter(description = "Filter charts by service name", |                         @Parameter(description = "Filter charts by service name", | ||||||
|                                 schema = @Schema(type = "string", example = "superset")) |                                 schema = @Schema(type = "string", example = "superset")) | ||||||
|                         @QueryParam("service") String serviceParam, |                         @QueryParam("service") String serviceParam, | ||||||
|                         @Parameter(description = "Limit the number topics returned. (1 to 1000000, default = 10)") |                         @Parameter(description = "Limit the number charts returned. (1 to 1000000, default = 10)") | ||||||
|                         @DefaultValue("10") |                         @DefaultValue("10") | ||||||
|                         @Min(1) |                         @Min(1) | ||||||
|                         @Max(1000000) |                         @Max(1000000) | ||||||
|                         @QueryParam("limit") int limitParam, |                         @QueryParam("limit") int limitParam, | ||||||
|                         @Parameter(description = "Returns list of topics before this cursor", |                         @Parameter(description = "Returns list of charts before this cursor", | ||||||
|                                 schema = @Schema(type = "string")) |                                 schema = @Schema(type = "string")) | ||||||
|                         @QueryParam("before") String before, |                         @QueryParam("before") String before, | ||||||
|                         @Parameter(description = "Returns list of topics after this cursor", |                         @Parameter(description = "Returns list of charts after this cursor", | ||||||
|                                 schema = @Schema(type = "string")) |                                 schema = @Schema(type = "string")) | ||||||
|                         @QueryParam("after") String after |                         @QueryParam("after") String after | ||||||
|   ) throws IOException, GeneralSecurityException { |   ) throws IOException, GeneralSecurityException { | ||||||
| @ -191,10 +188,10 @@ public class ChartResource { | |||||||
|   @Operation(summary = "Get a Chart", tags = "charts", |   @Operation(summary = "Get a Chart", tags = "charts", | ||||||
|           description = "Get a chart by `id`.", |           description = "Get a chart by `id`.", | ||||||
|           responses = { |           responses = { | ||||||
|                   @ApiResponse(responseCode = "200", description = "The topic", |                   @ApiResponse(responseCode = "200", description = "The chart", | ||||||
|                           content = @Content(mediaType = "application/json", |                           content = @Content(mediaType = "application/json", | ||||||
|                                   schema = @Schema(implementation = Dashboard.class))), |                                   schema = @Schema(implementation = Dashboard.class))), | ||||||
|                   @ApiResponse(responseCode = "404", description = "Topic for instance {id} is not found") |                   @ApiResponse(responseCode = "404", description = "Chart for instance {id} is not found") | ||||||
|           }) |           }) | ||||||
|   public Chart get(@Context UriInfo uriInfo, @PathParam("id") String id, |   public Chart get(@Context UriInfo uriInfo, @PathParam("id") String id, | ||||||
|                       @Context SecurityContext securityContext, |                       @Context SecurityContext securityContext, | ||||||
| @ -213,7 +210,7 @@ public class ChartResource { | |||||||
|                   @ApiResponse(responseCode = "200", description = "The chart", |                   @ApiResponse(responseCode = "200", description = "The chart", | ||||||
|                           content = @Content(mediaType = "application/json", |                           content = @Content(mediaType = "application/json", | ||||||
|                                   schema = @Schema(implementation = Chart.class))), |                                   schema = @Schema(implementation = Chart.class))), | ||||||
|                   @ApiResponse(responseCode = "404", description = "Topic for instance {id} is not found") |                   @ApiResponse(responseCode = "404", description = "Chart for instance {id} is not found") | ||||||
|           }) |           }) | ||||||
|   public Response getByName(@Context UriInfo uriInfo, @PathParam("fqn") String fqn, |   public Response getByName(@Context UriInfo uriInfo, @PathParam("fqn") String fqn, | ||||||
|                             @Context SecurityContext securityContext, |                             @Context SecurityContext securityContext, | ||||||
| @ -230,7 +227,7 @@ public class ChartResource { | |||||||
|   @Operation(summary = "Create a chart", tags = "charts", |   @Operation(summary = "Create a chart", tags = "charts", | ||||||
|           description = "Create a chart under an existing `service`.", |           description = "Create a chart under an existing `service`.", | ||||||
|           responses = { |           responses = { | ||||||
|                   @ApiResponse(responseCode = "200", description = "The topic", |                   @ApiResponse(responseCode = "200", description = "The chart", | ||||||
|                           content = @Content(mediaType = "application/json", |                           content = @Content(mediaType = "application/json", | ||||||
|                                   schema = @Schema(implementation = Chart.class))), |                                   schema = @Schema(implementation = Chart.class))), | ||||||
|                   @ApiResponse(responseCode = "400", description = "Bad request") |                   @ApiResponse(responseCode = "400", description = "Bad request") | ||||||
| @ -302,7 +299,7 @@ public class ChartResource { | |||||||
|           description = "Add a user identified by `userId` as followed of this chart", |           description = "Add a user identified by `userId` as followed of this chart", | ||||||
|           responses = { |           responses = { | ||||||
|                   @ApiResponse(responseCode = "200", description = "OK"), |                   @ApiResponse(responseCode = "200", description = "OK"), | ||||||
|                   @ApiResponse(responseCode = "404", description = "Topic for instance {id} is not found") |                   @ApiResponse(responseCode = "404", description = "Chart for instance {id} is not found") | ||||||
|           }) |           }) | ||||||
|   public Response addFollower(@Context UriInfo uriInfo, |   public Response addFollower(@Context UriInfo uriInfo, | ||||||
|                               @Context SecurityContext securityContext, |                               @Context SecurityContext securityContext, | ||||||
| @ -320,7 +317,7 @@ public class ChartResource { | |||||||
|   @DELETE |   @DELETE | ||||||
|   @Path("/{id}/followers/{userId}") |   @Path("/{id}/followers/{userId}") | ||||||
|   @Operation(summary = "Remove a follower", tags = "charts", |   @Operation(summary = "Remove a follower", tags = "charts", | ||||||
|           description = "Remove the user identified `userId` as a follower of the topic.") |           description = "Remove the user identified `userId` as a follower of the chart.") | ||||||
|   public Chart deleteFollower(@Context UriInfo uriInfo, |   public Chart deleteFollower(@Context UriInfo uriInfo, | ||||||
|                               @Context SecurityContext securityContext, |                               @Context SecurityContext securityContext, | ||||||
|                               @Parameter(description = "Id of the chart", |                               @Parameter(description = "Id of the chart", | ||||||
|  | |||||||
| @ -17,10 +17,16 @@ | |||||||
| package org.openmetadata.catalog.resources.dashboards; | package org.openmetadata.catalog.resources.dashboards; | ||||||
| 
 | 
 | ||||||
| import com.google.inject.Inject; | import com.google.inject.Inject; | ||||||
|  | import io.swagger.v3.oas.annotations.ExternalDocumentation; | ||||||
|  | import io.swagger.v3.oas.annotations.media.ExampleObject; | ||||||
|  | import io.swagger.v3.oas.annotations.parameters.RequestBody; | ||||||
|  | import org.openmetadata.catalog.api.data.CreateDashboard; | ||||||
|  | import org.openmetadata.catalog.entity.data.Chart; | ||||||
| import org.openmetadata.catalog.entity.data.Dashboard; | import org.openmetadata.catalog.entity.data.Dashboard; | ||||||
| import org.openmetadata.catalog.jdbi3.DashboardRepository; | import org.openmetadata.catalog.jdbi3.DashboardRepository; | ||||||
| import org.openmetadata.catalog.resources.Collection; | import org.openmetadata.catalog.resources.Collection; | ||||||
| import org.openmetadata.catalog.security.SecurityUtil; | import org.openmetadata.catalog.security.SecurityUtil; | ||||||
|  | import org.openmetadata.catalog.util.EntityUtil; | ||||||
| import org.openmetadata.catalog.util.EntityUtil.Fields; | import org.openmetadata.catalog.util.EntityUtil.Fields; | ||||||
| import org.openmetadata.catalog.util.RestUtil; | import org.openmetadata.catalog.util.RestUtil; | ||||||
| import org.openmetadata.catalog.util.RestUtil.PutResponse; | import org.openmetadata.catalog.util.RestUtil.PutResponse; | ||||||
| @ -33,9 +39,15 @@ import io.swagger.v3.oas.annotations.media.Schema; | |||||||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||||||
| import org.openmetadata.catalog.security.CatalogAuthorizer; | import org.openmetadata.catalog.security.CatalogAuthorizer; | ||||||
| 
 | 
 | ||||||
|  | import javax.json.JsonPatch; | ||||||
| import javax.validation.Valid; | import javax.validation.Valid; | ||||||
|  | import javax.validation.constraints.Max; | ||||||
|  | import javax.validation.constraints.Min; | ||||||
| import javax.ws.rs.Consumes; | import javax.ws.rs.Consumes; | ||||||
|  | import javax.ws.rs.DELETE; | ||||||
|  | import javax.ws.rs.DefaultValue; | ||||||
| import javax.ws.rs.GET; | import javax.ws.rs.GET; | ||||||
|  | import javax.ws.rs.PATCH; | ||||||
| import javax.ws.rs.POST; | import javax.ws.rs.POST; | ||||||
| import javax.ws.rs.PUT; | import javax.ws.rs.PUT; | ||||||
| import javax.ws.rs.Path; | import javax.ws.rs.Path; | ||||||
| @ -49,6 +61,9 @@ import javax.ws.rs.core.SecurityContext; | |||||||
| import javax.ws.rs.core.UriInfo; | import javax.ws.rs.core.UriInfo; | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.io.UnsupportedEncodingException; | ||||||
|  | import java.security.GeneralSecurityException; | ||||||
|  | import java.text.ParseException; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @ -87,19 +102,29 @@ public class DashboardResource { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static class DashboardList extends ResultList<Dashboard> { |   static class DashboardList extends ResultList<Dashboard> { | ||||||
|     DashboardList(List<Dashboard> data) { |     @SuppressWarnings("unused") | ||||||
|       super(data); |     DashboardList() { | ||||||
|  |       // Empty constructor needed for deserialization | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     DashboardList(List<Dashboard> data, int limitParam, String beforeCursor, | ||||||
|  |               String afterCursor) throws GeneralSecurityException, UnsupportedEncodingException { | ||||||
|  |       super(data, limitParam, beforeCursor, afterCursor); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static final String FIELDS ="owner,service,usageSummary"; |   static final String FIELDS = "owner,service,followers,tags,usageSummary"; | ||||||
|   public static final List<String> FIELD_LIST = Arrays.asList(FIELDS.replaceAll(" ", "") |   public static final List<String> FIELD_LIST = Arrays.asList(FIELDS.replaceAll(" ", "") | ||||||
|           .split(",")); |           .split(",")); | ||||||
|  | 
 | ||||||
|   @GET |   @GET | ||||||
|   @Operation(summary = "List dashboards", tags = "dashboards", |   @Valid | ||||||
|           description = "Get a list of dashboards. Use `fields` parameter to get only necessary fields.", |   @Operation(summary = "List Dashboards", tags = "dashboards", | ||||||
|  |           description = "Get a list of dashboards, optionally filtered by `service` it belongs to. Use `fields` " + | ||||||
|  |                   "parameter to get only necessary fields. Use cursor-based pagination to limit the number " + | ||||||
|  |                   "entries in the list using `limit` and `before` or `after` query params.", | ||||||
|           responses = { |           responses = { | ||||||
|                   @ApiResponse(responseCode = "200", description = "List of dashboards", |                   @ApiResponse(responseCode = "200", description = "List of dashboardss", | ||||||
|                                content = @Content(mediaType = "application/json", |                                content = @Content(mediaType = "application/json", | ||||||
|                                        schema = @Schema(implementation = DashboardList.class))) |                                        schema = @Schema(implementation = DashboardList.class))) | ||||||
|           }) |           }) | ||||||
| @ -107,9 +132,48 @@ public class DashboardResource { | |||||||
|                                       @Context SecurityContext securityContext, |                                       @Context SecurityContext securityContext, | ||||||
|                                       @Parameter(description = "Fields requested in the returned resource", |                                       @Parameter(description = "Fields requested in the returned resource", | ||||||
|                                               schema = @Schema(type = "string", example = FIELDS)) |                                               schema = @Schema(type = "string", example = FIELDS)) | ||||||
|                             @QueryParam("fields") String fieldsParam) throws IOException { |                                       @QueryParam("fields") String fieldsParam, | ||||||
|  |                                       @Parameter(description = "Filter dashboards by service name", | ||||||
|  |                                               schema = @Schema(type = "string", example = "superset")) | ||||||
|  |                                       @QueryParam("service") String serviceParam, | ||||||
|  |                                       @Parameter(description = "Limit the number dashboards returned. (1 to 1000000, default = 10)") | ||||||
|  |                                       @DefaultValue("10") | ||||||
|  |                                       @Min(1) | ||||||
|  |                                       @Max(1000000) | ||||||
|  |                                       @QueryParam("limit") int limitParam, | ||||||
|  |                                       @Parameter(description = "Returns list of dashboards before this cursor", | ||||||
|  |                                               schema = @Schema(type = "string")) | ||||||
|  |                                       @QueryParam("before") String before, | ||||||
|  |                                       @Parameter(description = "Returns list of dashboards after this cursor", | ||||||
|  |                                               schema = @Schema(type = "string")) | ||||||
|  |                                       @QueryParam("after") String after | ||||||
|  |   ) throws IOException, GeneralSecurityException { | ||||||
|  |     RestUtil.validateCursors(before, after); | ||||||
|     Fields fields = new Fields(FIELD_LIST, fieldsParam); |     Fields fields = new Fields(FIELD_LIST, fieldsParam); | ||||||
|     return new DashboardList(addHref(uriInfo, dao.list(fields))); | 
 | ||||||
|  |     List<Dashboard> dashboards; | ||||||
|  |     String beforeCursor = null, afterCursor = null; | ||||||
|  | 
 | ||||||
|  |     // For calculating cursors, ask for one extra entry beyond limit. If the extra entry exists, then in forward | ||||||
|  |     // scrolling afterCursor is not null. Similarly, if the extra entry exists, then in reverse scrolling, | ||||||
|  |     // beforeCursor is not null. Remove the extra entry before returning results. | ||||||
|  |     if (before != null) { // Reverse paging | ||||||
|  |       dashboards = dao.listBefore(fields, serviceParam, limitParam + 1, before); // Ask for one extra entry | ||||||
|  |       if (dashboards.size() > limitParam) { | ||||||
|  |         dashboards.remove(0); | ||||||
|  |         beforeCursor = dashboards.get(0).getFullyQualifiedName(); | ||||||
|  |       } | ||||||
|  |       afterCursor = dashboards.get(dashboards.size() - 1).getFullyQualifiedName(); | ||||||
|  |     } else { // Forward paging or first page | ||||||
|  |       dashboards = dao.listAfter(fields, serviceParam, limitParam + 1, after); | ||||||
|  |       beforeCursor = after == null ? null : dashboards.get(0).getFullyQualifiedName(); | ||||||
|  |       if (dashboards.size() > limitParam) { | ||||||
|  |         dashboards.remove(limitParam); | ||||||
|  |         afterCursor = dashboards.get(limitParam - 1).getFullyQualifiedName(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     addHref(uriInfo, dashboards); | ||||||
|  |     return new DashboardList(dashboards, limitParam, beforeCursor, afterCursor); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @GET |   @GET | ||||||
| @ -142,13 +206,40 @@ public class DashboardResource { | |||||||
|                   @ApiResponse(responseCode = "400", description = "Bad request") |                   @ApiResponse(responseCode = "400", description = "Bad request") | ||||||
|           }) |           }) | ||||||
|   public Response create(@Context UriInfo uriInfo, @Context SecurityContext securityContext, |   public Response create(@Context UriInfo uriInfo, @Context SecurityContext securityContext, | ||||||
|                          @Valid Dashboard dashboard) throws IOException { |                          @Valid CreateDashboard create) throws IOException { | ||||||
|     SecurityUtil.checkAdminOrBotRole(authorizer, securityContext); |     SecurityUtil.checkAdminOrBotRole(authorizer, securityContext); | ||||||
|     dashboard.setId(UUID.randomUUID()); |     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())); |     addHref(uriInfo, dao.create(dashboard, dashboard.getService(), dashboard.getOwner())); | ||||||
|     return Response.created(dashboard.getHref()).entity(dashboard).build(); |     return Response.created(dashboard.getHref()).entity(dashboard).build(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @PATCH | ||||||
|  |   @Path("/{id}") | ||||||
|  |   @Operation(summary = "Update a Dashboard", tags = "dashboards", | ||||||
|  |           description = "Update an existing dashboard using JsonPatch.", | ||||||
|  |           externalDocs = @ExternalDocumentation(description = "JsonPatch RFC", | ||||||
|  |                   url = "https://tools.ietf.org/html/rfc6902")) | ||||||
|  |   @Consumes(MediaType.APPLICATION_JSON_PATCH_JSON) | ||||||
|  |   public Dashboard updateDescription(@Context UriInfo uriInfo, | ||||||
|  |                                      @Context SecurityContext securityContext, | ||||||
|  |                                      @PathParam("id") String id, | ||||||
|  |                                      @RequestBody(description = "JsonPatch with array of operations", | ||||||
|  |                                          content = @Content(mediaType = MediaType.APPLICATION_JSON_PATCH_JSON, | ||||||
|  |                                                  examples = {@ExampleObject("[" + | ||||||
|  |                                                          "{op:remove, path:/a}," + | ||||||
|  |                                                          "{op:add, path: /b, value: val}" + | ||||||
|  |                                                          "]")})) | ||||||
|  |                                          JsonPatch patch) throws IOException { | ||||||
|  |     Fields fields = new Fields(FIELD_LIST, FIELDS); | ||||||
|  |     Dashboard dashboard = dao.get(id, fields); | ||||||
|  |     SecurityUtil.checkAdminRoleOrPermissions(authorizer, securityContext, | ||||||
|  |             EntityUtil.getEntityReference(dashboard)); | ||||||
|  |     dashboard = dao.patch(id, patch); | ||||||
|  |     return addHref(uriInfo, dashboard); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @PUT |   @PUT | ||||||
|   @Operation(summary = "Create or update a dashboard", tags = "dashboards", |   @Operation(summary = "Create or update a dashboard", tags = "dashboards", | ||||||
|           description = "Create a new dashboard, if it does not exist or update an existing dashboard.", |           description = "Create a new dashboard, if it does not exist or update an existing dashboard.", | ||||||
| @ -159,10 +250,65 @@ public class DashboardResource { | |||||||
|                   @ApiResponse(responseCode = "400", description = "Bad request") |                   @ApiResponse(responseCode = "400", description = "Bad request") | ||||||
|           }) |           }) | ||||||
|   public Response createOrUpdate(@Context UriInfo uriInfo, @Context SecurityContext securityContext, |   public Response createOrUpdate(@Context UriInfo uriInfo, @Context SecurityContext securityContext, | ||||||
|                                  @Valid Dashboard dashboard) throws IOException { |                                  @Valid Dashboard create) throws IOException { | ||||||
|     dashboard.setId(UUID.randomUUID()); |     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())); | ||||||
|     PutResponse<Dashboard> response = dao.createOrUpdate(dashboard, dashboard.getService(), dashboard.getOwner()); |     PutResponse<Dashboard> response = dao.createOrUpdate(dashboard, dashboard.getService(), dashboard.getOwner()); | ||||||
|     addHref(uriInfo, response.getEntity()); |     addHref(uriInfo, response.getEntity()); | ||||||
|     return Response.status(response.getStatus()).entity(response.getEntity()).build(); |     return Response.status(response.getStatus()).entity(response.getEntity()).build(); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   @PUT | ||||||
|  |   @Path("/{id}/followers") | ||||||
|  |   @Operation(summary = "Add a follower", tags = "dashboards", | ||||||
|  |           description = "Add a user identified by `userId` as follower of this dashboard", | ||||||
|  |           responses = { | ||||||
|  |                   @ApiResponse(responseCode = "200", description = "OK"), | ||||||
|  |                   @ApiResponse(responseCode = "404", description = "Dashboard for instance {id} is not found") | ||||||
|  |           }) | ||||||
|  |   public Response addFollower(@Context UriInfo uriInfo, | ||||||
|  |                               @Context SecurityContext securityContext, | ||||||
|  |                               @Parameter(description = "Id of the dashboard", schema = @Schema(type = "string")) | ||||||
|  |                               @PathParam("id") String id, | ||||||
|  |                               @Parameter(description = "Id of the user to be added as follower", | ||||||
|  |                                       schema = @Schema(type = "string")) | ||||||
|  |                                       String userId) throws IOException, ParseException { | ||||||
|  |     Fields fields = new Fields(FIELD_LIST, "followers"); | ||||||
|  |     Response.Status status = dao.addFollower(id, userId); | ||||||
|  |     Dashboard dashboard = dao.get(id, fields); | ||||||
|  |     return Response.status(status).entity(dashboard).build(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @DELETE | ||||||
|  |   @Path("/{id}/followers/{userId}") | ||||||
|  |   @Operation(summary = "Remove a follower", tags = "dashboards", | ||||||
|  |           description = "Remove the user identified `userId` as a follower of the dashboard.") | ||||||
|  |   public Dashboard deleteFollower(@Context UriInfo uriInfo, | ||||||
|  |                               @Context SecurityContext securityContext, | ||||||
|  |                               @Parameter(description = "Id of the dashboard", | ||||||
|  |                                       schema = @Schema(type = "string")) | ||||||
|  |                               @PathParam("id") String id, | ||||||
|  |                               @Parameter(description = "Id of the user being removed as follower", | ||||||
|  |                                       schema = @Schema(type = "string")) | ||||||
|  |                               @PathParam("userId") String userId) throws IOException, ParseException { | ||||||
|  |     Fields fields = new Fields(FIELD_LIST, "followers"); | ||||||
|  |     dao.deleteFollower(id, userId); | ||||||
|  |     Dashboard dashboard = dao.get(id, fields); | ||||||
|  |     return addHref(uriInfo, dashboard); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @DELETE | ||||||
|  |   @Path("/{id}") | ||||||
|  |   @Operation(summary = "Delete a Dashboard", tags = "dashboards", | ||||||
|  |           description = "Delete a dashboard by `id`.", | ||||||
|  |           responses = { | ||||||
|  |                   @ApiResponse(responseCode = "200", description = "OK"), | ||||||
|  |                   @ApiResponse(responseCode = "404", description = "Dashboard for instance {id} is not found") | ||||||
|  |           }) | ||||||
|  |   public Response delete(@Context UriInfo uriInfo, @PathParam("id") String id) { | ||||||
|  |     dao.delete(id); | ||||||
|  |     return Response.ok().build(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -28,6 +28,14 @@ | |||||||
|       }, |       }, | ||||||
|       "default": null |       "default": null | ||||||
|     }, |     }, | ||||||
|  |     "tags": { | ||||||
|  |       "description": "Tags for this chart", | ||||||
|  |       "type": "array", | ||||||
|  |       "items": { | ||||||
|  |         "$ref": "../../type/tagLabel.json" | ||||||
|  |       }, | ||||||
|  |       "default": null | ||||||
|  |     }, | ||||||
|     "owner": { |     "owner": { | ||||||
|       "description": "Owner of this database", |       "description": "Owner of this database", | ||||||
|       "$ref": "../../type/entityReference.json" |       "$ref": "../../type/entityReference.json" | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Suresh Srinivas
						Suresh Srinivas