Add Voting to Entities (#13101)

* Add Voting to Entities

* Fix Failing test and maven build failure in main

* revert files
This commit is contained in:
Mohit Yadav 2023-09-08 16:23:32 +05:30 committed by GitHub
parent cc5128fcb3
commit e70f7afe4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 383 additions and 12 deletions

View File

@ -244,6 +244,10 @@ public abstract class EntityRepository<T extends EntityInterface> {
this.putFields.addField(allowedFields, FIELD_EXTENSION); this.putFields.addField(allowedFields, FIELD_EXTENSION);
} }
this.supportsVotes = allowedFields.contains(FIELD_VOTES); this.supportsVotes = allowedFields.contains(FIELD_VOTES);
if (supportsVotes) {
this.patchFields.addField(allowedFields, FIELD_VOTES);
this.putFields.addField(allowedFields, FIELD_VOTES);
}
this.supportsDomain = allowedFields.contains(FIELD_DOMAIN); this.supportsDomain = allowedFields.contains(FIELD_DOMAIN);
if (supportsDomain) { if (supportsDomain) {
this.patchFields.addField(allowedFields, FIELD_DOMAIN); this.patchFields.addField(allowedFields, FIELD_DOMAIN);
@ -670,6 +674,7 @@ public abstract class EntityRepository<T extends EntityInterface> {
entity.setChildren(fields.contains(FIELD_CHILDREN) ? getChildren(entity) : entity.getChildren()); entity.setChildren(fields.contains(FIELD_CHILDREN) ? getChildren(entity) : entity.getChildren());
entity.setExperts(fields.contains(FIELD_EXPERTS) ? getExperts(entity) : entity.getExperts()); entity.setExperts(fields.contains(FIELD_EXPERTS) ? getExperts(entity) : entity.getExperts());
entity.setReviewers(fields.contains(FIELD_REVIEWERS) ? getReviewers(entity) : entity.getReviewers()); entity.setReviewers(fields.contains(FIELD_REVIEWERS) ? getReviewers(entity) : entity.getReviewers());
entity.setVotes(fields.contains(FIELD_VOTES) ? getVotes(entity) : entity.getVotes());
setFields(entity, fields); setFields(entity, fields);
return entity; return entity;
} }
@ -684,6 +689,7 @@ public abstract class EntityRepository<T extends EntityInterface> {
entity.setChildren(fields.contains(FIELD_CHILDREN) ? entity.getChildren() : null); entity.setChildren(fields.contains(FIELD_CHILDREN) ? entity.getChildren() : null);
entity.setExperts(fields.contains(FIELD_EXPERTS) ? entity.getExperts() : null); entity.setExperts(fields.contains(FIELD_EXPERTS) ? entity.getExperts() : null);
entity.setReviewers(fields.contains(FIELD_REVIEWERS) ? entity.getReviewers() : null); entity.setReviewers(fields.contains(FIELD_REVIEWERS) ? entity.getReviewers() : null);
entity.setVotes(fields.contains(FIELD_VOTES) ? entity.getVotes() : null);
clearFields(entity, fields); clearFields(entity, fields);
return entity; return entity;
} }

View File

@ -27,10 +27,9 @@ import org.openmetadata.service.util.RestUtil;
public class QueryRepository extends EntityRepository<Query> { public class QueryRepository extends EntityRepository<Query> {
private static final String QUERY_USED_IN_FIELD = "queryUsedIn"; private static final String QUERY_USED_IN_FIELD = "queryUsedIn";
private static final String QUERY_USERS_FIELD = "users"; private static final String QUERY_USERS_FIELD = "users";
private static final String QUERY_PATCH_FIELDS = "users,query,queryUsedIn"; private static final String QUERY_PATCH_FIELDS = "users,query,queryUsedIn";
private static final String QUERY_UPDATE_FIELDS = "users,votes,queryUsedIn"; private static final String QUERY_UPDATE_FIELDS = "users,queryUsedIn";
public QueryRepository(CollectionDAO dao) { public QueryRepository(CollectionDAO dao) {
super( super(
@ -45,14 +44,12 @@ public class QueryRepository extends EntityRepository<Query> {
@Override @Override
public Query setFields(Query entity, EntityUtil.Fields fields) { public Query setFields(Query entity, EntityUtil.Fields fields) {
entity.setVotes(fields.contains("votes") ? getVotes(entity) : entity.getVotes());
entity.setQueryUsedIn(fields.contains(QUERY_USED_IN_FIELD) ? getQueryUsage(entity) : entity.getQueryUsedIn()); entity.setQueryUsedIn(fields.contains(QUERY_USED_IN_FIELD) ? getQueryUsage(entity) : entity.getQueryUsedIn());
return entity.withUsers(fields.contains("users") ? getQueryUsers(entity) : entity.getUsers()); return entity.withUsers(fields.contains("users") ? getQueryUsers(entity) : entity.getUsers());
} }
@Override @Override
public Query clearFields(Query entity, EntityUtil.Fields fields) { public Query clearFields(Query entity, EntityUtil.Fields fields) {
entity.withVotes(fields.contains("votes") ? entity.getVotes() : null);
entity.withQueryUsedIn(fields.contains(QUERY_USED_IN_FIELD) ? entity.getQueryUsedIn() : null); entity.withQueryUsedIn(fields.contains(QUERY_USED_IN_FIELD) ? entity.getQueryUsedIn() : null);
return entity.withUsers(fields.contains("users") ? this.getQueryUsers(entity) : null); return entity.withUsers(fields.contains("users") ? this.getQueryUsers(entity) : null);
} }

View File

@ -59,7 +59,7 @@ public abstract class EntityResource<T extends EntityInterface, K extends Entity
allowedFields = repository.getAllowedFields(); allowedFields = repository.getAllowedFields();
this.repository = repository; this.repository = repository;
this.authorizer = authorizer; this.authorizer = authorizer;
addViewOperation("owner,followers,tags,extension", VIEW_BASIC); addViewOperation("owner,followers,votes,tags,extension", VIEW_BASIC);
Entity.registerEntity(entityClass, entityType, repository, getEntitySpecificOperations()); Entity.registerEntity(entityClass, entityType, repository, getEntitySpecificOperations());
} }
@ -344,7 +344,7 @@ public abstract class EntityResource<T extends EntityInterface, K extends Entity
for (String field : fields) { for (String field : fields) {
if (allowedFields.contains(field)) { if (allowedFields.contains(field)) {
fieldsToViewOperations.put(field, operation); fieldsToViewOperations.put(field, operation);
} else if (!"owner,followers,tags,extension".contains(field)) { } else if (!"owner,followers,votes,tags,extension".contains(field)) {
// Some common fields for all the entities might be missing. Ignore it. // Some common fields for all the entities might be missing. Ignore it.
throw new IllegalArgumentException(CatalogExceptionMessage.invalidField(field)); throw new IllegalArgumentException(CatalogExceptionMessage.invalidField(field));
} }

View File

@ -46,9 +46,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateChart; import org.openmetadata.schema.api.data.CreateChart;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.Chart; import org.openmetadata.schema.entity.data.Chart;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation; import org.openmetadata.schema.type.MetadataOperation;
@ -386,6 +388,27 @@ public class ChartResource extends EntityResource<Chart, ChartRepository> {
return deleteByName(uriInfo, securityContext, fqn, false, hardDelete); return deleteByName(uriInfo, securityContext, fqn, false, hardDelete);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@PUT @PUT
@Path("/restore") @Path("/restore")
@Operation( @Operation(

View File

@ -46,9 +46,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateDashboard; import org.openmetadata.schema.api.data.CreateDashboard;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.Dashboard; import org.openmetadata.schema.entity.data.Dashboard;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation; import org.openmetadata.schema.type.MetadataOperation;
@ -353,6 +355,27 @@ public class DashboardResource extends EntityResource<Dashboard, DashboardReposi
return repository.deleteFollower(securityContext.getUserPrincipal().getName(), id, userId).toResponse(); return repository.deleteFollower(securityContext.getUserPrincipal().getName(), id, userId).toResponse();
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@Operation( @Operation(

View File

@ -46,9 +46,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateDatabase; import org.openmetadata.schema.api.data.CreateDatabase;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.Database; import org.openmetadata.schema.entity.data.Database;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation; import org.openmetadata.schema.type.MetadataOperation;
@ -339,6 +341,27 @@ public class DatabaseResource extends EntityResource<Database, DatabaseRepositor
return delete(uriInfo, securityContext, id, recursive, hardDelete); return delete(uriInfo, securityContext, id, recursive, hardDelete);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/name/{fqn}") @Path("/name/{fqn}")
@Operation( @Operation(

View File

@ -46,9 +46,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateDatabaseSchema; import org.openmetadata.schema.api.data.CreateDatabaseSchema;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.DatabaseSchema; import org.openmetadata.schema.entity.data.DatabaseSchema;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation; import org.openmetadata.schema.type.MetadataOperation;
@ -319,6 +321,27 @@ public class DatabaseSchemaResource extends EntityResource<DatabaseSchema, Datab
return createOrUpdate(uriInfo, securityContext, schema); return createOrUpdate(uriInfo, securityContext, schema);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@Operation( @Operation(

View File

@ -16,6 +16,7 @@ import javax.validation.constraints.Max;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
import javax.ws.rs.*; import javax.ws.rs.*;
import javax.ws.rs.core.*; import javax.ws.rs.core.*;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateStoredProcedure; import org.openmetadata.schema.api.data.CreateStoredProcedure;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.DatabaseSchema; import org.openmetadata.schema.entity.data.DatabaseSchema;
@ -328,6 +329,27 @@ public class StoredProcedureResource extends EntityResource<StoredProcedure, Sto
.toResponse(); .toResponse();
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@Operation( @Operation(

View File

@ -47,6 +47,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateTable; import org.openmetadata.schema.api.data.CreateTable;
import org.openmetadata.schema.api.data.CreateTableProfile; import org.openmetadata.schema.api.data.CreateTableProfile;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
@ -856,6 +857,27 @@ public class TableResource extends EntityResource<Table, TableRepository> {
return addHref(uriInfo, table); return addHref(uriInfo, table);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}/customMetric/{columnName}/{customMetricName}") @Path("/{id}/customMetric/{columnName}/{customMetricName}")
@Operation( @Operation(

View File

@ -43,9 +43,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateDashboardDataModel; import org.openmetadata.schema.api.data.CreateDashboardDataModel;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.DashboardDataModel; import org.openmetadata.schema.entity.data.DashboardDataModel;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
import org.openmetadata.service.Entity; import org.openmetadata.service.Entity;
@ -353,6 +355,27 @@ public class DashboardDataModelResource extends EntityResource<DashboardDataMode
return repository.deleteFollower(securityContext.getUserPrincipal().getName(), id, userId).toResponse(); return repository.deleteFollower(securityContext.getUserPrincipal().getName(), id, userId).toResponse();
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@Operation( @Operation(

View File

@ -45,9 +45,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateGlossary; import org.openmetadata.schema.api.data.CreateGlossary;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.Glossary; import org.openmetadata.schema.entity.data.Glossary;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation; import org.openmetadata.schema.type.MetadataOperation;
@ -301,6 +303,27 @@ public class GlossaryResource extends EntityResource<Glossary, GlossaryRepositor
return createOrUpdate(uriInfo, securityContext, glossary); return createOrUpdate(uriInfo, securityContext, glossary);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@Operation( @Operation(

View File

@ -44,10 +44,12 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateGlossaryTerm; import org.openmetadata.schema.api.data.CreateGlossaryTerm;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.Glossary; import org.openmetadata.schema.entity.data.Glossary;
import org.openmetadata.schema.entity.data.GlossaryTerm; import org.openmetadata.schema.entity.data.GlossaryTerm;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
@ -354,6 +356,27 @@ public class GlossaryTermResource extends EntityResource<GlossaryTerm, GlossaryT
return createOrUpdate(uriInfo, securityContext, term); return createOrUpdate(uriInfo, securityContext, term);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@Operation( @Operation(

View File

@ -41,7 +41,9 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.entity.data.Metrics; import org.openmetadata.schema.entity.data.Metrics;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation; import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.service.jdbi3.CollectionDAO; import org.openmetadata.service.jdbi3.CollectionDAO;
@ -159,6 +161,27 @@ public class MetricsResource extends EntityResource<Metrics, MetricsRepository>
return super.create(uriInfo, securityContext, metrics); return super.create(uriInfo, securityContext, metrics);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@Override @Override
@PUT @PUT
@Operation( @Operation(

View File

@ -72,7 +72,7 @@ public class QueryResource extends EntityResource<Query, QueryRepository> {
@Override @Override
protected List<MetadataOperation> getEntitySpecificOperations() { protected List<MetadataOperation> getEntitySpecificOperations() {
addViewOperation("users,votes,queryUsedIn", MetadataOperation.VIEW_BASIC); addViewOperation("users,queryUsedIn", MetadataOperation.VIEW_BASIC);
return null; return null;
} }
@ -321,9 +321,9 @@ public class QueryResource extends EntityResource<Query, QueryRepository> {
@PUT @PUT
@Path("/{id}/vote") @Path("/{id}/vote")
@Operation( @Operation(
operationId = "updateVote", operationId = "updateVoteForEntity",
summary = "Update Vote for a query", summary = "Update Vote for a Entity",
description = "Update vote for a query", description = "Update vote for a Entity",
responses = { responses = {
@ApiResponse( @ApiResponse(
responseCode = "200", responseCode = "200",
@ -334,7 +334,7 @@ public class QueryResource extends EntityResource<Query, QueryRepository> {
public Response updateVote( public Response updateVote(
@Context UriInfo uriInfo, @Context UriInfo uriInfo,
@Context SecurityContext securityContext, @Context SecurityContext securityContext,
@Parameter(description = "Id of the Query", schema = @Schema(type = "UUID")) @PathParam("id") UUID id, @Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) { @Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse(); return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
} }

View File

@ -39,7 +39,9 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.entity.data.Report; import org.openmetadata.schema.entity.data.Report;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation; import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.service.jdbi3.CollectionDAO; import org.openmetadata.service.jdbi3.CollectionDAO;
@ -170,6 +172,27 @@ public class ReportResource extends EntityResource<Report, ReportRepository> {
return super.createOrUpdate(uriInfo, securityContext, report); return super.createOrUpdate(uriInfo, securityContext, report);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
private void addToReport(SecurityContext securityContext, Report report) { private void addToReport(SecurityContext securityContext, Report report) {
report report
.withId(UUID.randomUUID()) .withId(UUID.randomUUID())

View File

@ -46,6 +46,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateSearchIndex; import org.openmetadata.schema.api.data.CreateSearchIndex;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.SearchIndex; import org.openmetadata.schema.entity.data.SearchIndex;
@ -407,6 +408,27 @@ public class SearchIndexResource extends EntityResource<SearchIndex, SearchIndex
.toResponse(); .toResponse();
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@Operation( @Operation(

View File

@ -31,6 +31,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateContainer; import org.openmetadata.schema.api.data.CreateContainer;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.Container; import org.openmetadata.schema.entity.data.Container;
@ -379,6 +380,27 @@ public class ContainerResource extends EntityResource<Container, ContainerReposi
return delete(uriInfo, securityContext, id, false, hardDelete); return delete(uriInfo, securityContext, id, false, hardDelete);
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/name/{fqn}") @Path("/name/{fqn}")
@Operation( @Operation(

View File

@ -46,6 +46,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.openmetadata.schema.api.VoteRequest;
import org.openmetadata.schema.api.data.CreateTopic; import org.openmetadata.schema.api.data.CreateTopic;
import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.RestoreEntity;
import org.openmetadata.schema.entity.data.Topic; import org.openmetadata.schema.entity.data.Topic;
@ -407,6 +408,27 @@ public class TopicResource extends EntityResource<Topic, TopicRepository> {
.toResponse(); .toResponse();
} }
@PUT
@Path("/{id}/vote")
@Operation(
operationId = "updateVoteForEntity",
summary = "Update Vote for a Entity",
description = "Update vote for a Entity",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "model for instance {id} is not found")
})
public Response updateVote(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid VoteRequest request) {
return repository.updateVote(securityContext.getUserPrincipal().getName(), id, request).toResponse();
}
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@Operation( @Operation(

View File

@ -39,6 +39,7 @@ import static org.openmetadata.service.Entity.FIELD_EXTENSION;
import static org.openmetadata.service.Entity.FIELD_FOLLOWERS; import static org.openmetadata.service.Entity.FIELD_FOLLOWERS;
import static org.openmetadata.service.Entity.FIELD_OWNER; import static org.openmetadata.service.Entity.FIELD_OWNER;
import static org.openmetadata.service.Entity.FIELD_TAGS; import static org.openmetadata.service.Entity.FIELD_TAGS;
import static org.openmetadata.service.Entity.FIELD_VOTES;
import static org.openmetadata.service.exception.CatalogExceptionMessage.ENTITY_ALREADY_EXISTS; import static org.openmetadata.service.exception.CatalogExceptionMessage.ENTITY_ALREADY_EXISTS;
import static org.openmetadata.service.exception.CatalogExceptionMessage.entityIsNotEmpty; import static org.openmetadata.service.exception.CatalogExceptionMessage.entityIsNotEmpty;
import static org.openmetadata.service.exception.CatalogExceptionMessage.entityNotFound; import static org.openmetadata.service.exception.CatalogExceptionMessage.entityNotFound;
@ -224,6 +225,7 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr
private final String allFields; private final String allFields;
private final String systemEntityName; // System entity provided by the system that can't be deleted private final String systemEntityName; // System entity provided by the system that can't be deleted
protected final boolean supportsFollowers; protected final boolean supportsFollowers;
protected final boolean supportsVotes;
protected final boolean supportsOwner; protected final boolean supportsOwner;
protected final boolean supportsTags; protected final boolean supportsTags;
protected boolean supportsPatch = true; protected boolean supportsPatch = true;
@ -393,6 +395,7 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr
Set<String> allowedFields = Entity.getEntityFields(entityClass); Set<String> allowedFields = Entity.getEntityFields(entityClass);
this.supportsEmptyDescription = !EntityUtil.isDescriptionRequired(entityClass); this.supportsEmptyDescription = !EntityUtil.isDescriptionRequired(entityClass);
this.supportsFollowers = allowedFields.contains(FIELD_FOLLOWERS); this.supportsFollowers = allowedFields.contains(FIELD_FOLLOWERS);
this.supportsVotes = allowedFields.contains(FIELD_VOTES);
this.supportsOwner = allowedFields.contains(FIELD_OWNER); this.supportsOwner = allowedFields.contains(FIELD_OWNER);
this.supportsTags = allowedFields.contains(FIELD_TAGS); this.supportsTags = allowedFields.contains(FIELD_TAGS);
this.supportsSoftDelete = allowedFields.contains(FIELD_DELETED); this.supportsSoftDelete = allowedFields.contains(FIELD_DELETED);

View File

@ -160,6 +160,10 @@ public interface EntityInterface {
/* no-op implementation to be overridden */ /* no-op implementation to be overridden */
} }
default void setVotes(Votes vote) {
/* no-op implementation to be overridden */
}
<T extends EntityInterface> T withHref(URI href); <T extends EntityInterface> T withHref(URI href);
@JsonIgnore @JsonIgnore

View File

@ -147,6 +147,9 @@
"description": "List of data products this entity is part of.", "description": "List of data products this entity is part of.",
"$ref" : "../../type/entityReferenceList.json" "$ref" : "../../type/entityReferenceList.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"

View File

@ -176,6 +176,9 @@
"description": "Domain the Container belongs to. When not set, the Container inherits the domain from the storage service it belongs to.", "description": "Domain the Container belongs to. When not set, the Container inherits the domain from the storage service it belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"

View File

@ -135,6 +135,9 @@
"description": "List of data products this entity is part of.", "description": "List of data products this entity is part of.",
"$ref" : "../../type/entityReferenceList.json" "$ref" : "../../type/entityReferenceList.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"

View File

@ -142,6 +142,9 @@
"description": "Domain the Dashboard Data Model belongs to. When not set, the Dashboard model inherits the domain from the dashboard service it belongs to.", "description": "Domain the Dashboard Data Model belongs to. When not set, the Dashboard model inherits the domain from the dashboard service it belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"

View File

@ -116,6 +116,9 @@
"description": "Domain the Database belongs to. When not set, the Database inherits the domain from the database service it belongs to.", "description": "Domain the Database belongs to. When not set, the Database inherits the domain from the database service it belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"

View File

@ -111,6 +111,9 @@
"description": "Domain the Database Schema belongs to. When not set, the Schema inherits the domain from the database it belongs to.", "description": "Domain the Database Schema belongs to. When not set, the Schema inherits the domain from the database it belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"

View File

@ -95,6 +95,9 @@
"domain" : { "domain" : {
"description": "Domain the Glossary belongs to.", "description": "Domain the Glossary belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
},
"votes" : {
"$ref": "../../type/votes.json"
} }
}, },
"required": ["id", "name", "description"], "required": ["id", "name", "description"],

View File

@ -143,6 +143,9 @@
"domain" : { "domain" : {
"description": "Domain the Glossary Term belongs to. When not set, the Glossary TErm inherits the domain from the Glossary it belongs to.", "description": "Domain the Glossary Term belongs to. When not set, the Glossary TErm inherits the domain from the Glossary it belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
},
"votes" : {
"$ref": "../../type/votes.json"
} }
}, },
"required": ["id", "name", "description", "glossary"], "required": ["id", "name", "description", "glossary"],

View File

@ -76,6 +76,9 @@
"domain" : { "domain" : {
"description": "Domain the Metrics belongs to.", "description": "Domain the Metrics belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
},
"votes" : {
"$ref": "../../type/votes.json"
} }
}, },
"required": ["id", "name", "service"], "required": ["id", "name", "service"],

View File

@ -273,11 +273,13 @@
"description": "Domain the MLModel belongs to. When not set, the MLModel inherits the domain from the ML Model Service it belongs to.", "description": "Domain the MLModel belongs to. When not set, the MLModel inherits the domain from the ML Model Service it belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"
} }
}, },
"required": ["id", "name", "algorithm", "service"], "required": ["id", "name", "algorithm", "service"],
"additionalProperties": false "additionalProperties": false

View File

@ -259,6 +259,9 @@
"description": "Domain the Pipeline belongs to. When not set, the pipeline inherits the domain from the Pipeline service it belongs to.", "description": "Domain the Pipeline belongs to. When not set, the pipeline inherits the domain from the Pipeline service it belongs to.",
"$ref": "../../type/entityReference.json" "$ref": "../../type/entityReference.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"

View File

@ -64,6 +64,9 @@
"description": "When `true` indicates the entity has been soft deleted.", "description": "When `true` indicates the entity has been soft deleted.",
"type": "boolean", "type": "boolean",
"default": false "default": false
},
"votes" : {
"$ref": "../../type/votes.json"
} }
}, },
"required": ["id", "name", "service"], "required": ["id", "name", "service"],

View File

@ -226,6 +226,9 @@
"dataProducts" : { "dataProducts" : {
"description": "List of data products this entity is part of.", "description": "List of data products this entity is part of.",
"$ref" : "../../type/entityReferenceList.json" "$ref" : "../../type/entityReferenceList.json"
},
"votes" : {
"$ref": "../../type/votes.json"
} }
}, },
"required": ["id", "name", "service", "fields"], "required": ["id", "name", "service", "fields"],

View File

@ -1033,6 +1033,9 @@
"description": "File format in case of file/datalake tables.", "description": "File format in case of file/datalake tables.",
"$ref" : "#/definitions/fileFormat" "$ref" : "#/definitions/fileFormat"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"

View File

@ -166,6 +166,9 @@
"description": "List of data products this entity is part of.", "description": "List of data products this entity is part of.",
"$ref" : "../../type/entityReferenceList.json" "$ref" : "../../type/entityReferenceList.json"
}, },
"votes" : {
"$ref": "../../type/votes.json"
},
"lifeCycle": { "lifeCycle": {
"description": "Life Cycle properties of the entity", "description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json" "$ref": "../../type/lifeCycle.json"