From cfd548556e0201e42d8b3e7db1cad28d9b591014 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Thu, 5 Jun 2025 11:17:13 +0530 Subject: [PATCH] Support followers in Domain and DataProduct (#21478) * Support followers in DataProduct * Support followers in domain --- .../domains/DataProductResource.java | 63 ++++++++++++++++++- .../resources/domains/DomainResource.java | 60 +++++++++++++++++- .../domains/DataProductResourceTest.java | 2 +- .../resources/domains/DomainResourceTest.java | 2 +- .../schema/entity/domains/dataProduct.json | 4 ++ .../json/schema/entity/domains/domain.json | 4 ++ .../generated/entity/domains/dataProduct.ts | 4 ++ .../ui/src/generated/entity/domains/domain.ts | 4 ++ 8 files changed, 139 insertions(+), 4 deletions(-) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java index 1cd0b30ce41..2b520727394 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java @@ -78,7 +78,7 @@ import org.openmetadata.service.util.ResultList; public class DataProductResource extends EntityResource { public static final String COLLECTION_PATH = "/v1/dataProducts/"; private final DataProductMapper mapper = new DataProductMapper(); - static final String FIELDS = "domain,owners,experts,assets,extension,tags"; + static final String FIELDS = "domain,owners,experts,assets,extension,tags,followers"; public DataProductResource(Authorizer authorizer, Limits limits) { super(Entity.DATA_PRODUCT, authorizer, limits); @@ -496,4 +496,65 @@ public class DataProductResource extends EntityResource { public static final String COLLECTION_PATH = "/v1/domains/"; private final DomainMapper mapper = new DomainMapper(); - static final String FIELDS = "tags,children,owners,experts,extension"; + static final String FIELDS = "tags,children,owners,experts,extension,followers"; public DomainResource(Authorizer authorizer, Limits limits) { super(Entity.DOMAIN, authorizer, limits); @@ -497,4 +497,62 @@ public class DomainResource extends EntityResource { return new EntityHierarchyList(repository.buildHierarchy(fieldsParam, limitParam)); } + + @PUT + @Path("/{id}/followers") + @Operation( + operationId = "addFollowerToDomain", + summary = "Add a follower", + description = "Add a user identified by `userId` as followed of this Domain", + responses = { + @ApiResponse( + responseCode = "200", + description = "OK", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = ChangeEvent.class))), + @ApiResponse(responseCode = "404", description = "Domain for instance {id} is not found") + }) + public Response addFollower( + @Context SecurityContext securityContext, + @Parameter(description = "Id of the Domain", schema = @Schema(type = "UUID")) @PathParam("id") + UUID id, + @Parameter( + description = "Id of the user to be added as follower", + schema = @Schema(type = "string")) + UUID userId) { + return repository + .addFollower(securityContext.getUserPrincipal().getName(), id, userId) + .toResponse(); + } + + @DELETE + @Path("/{id}/followers/{userId}") + @Operation( + operationId = "deleteFollowerFromDomain", + summary = "Remove a follower", + description = "Remove the user identified `userId` as a follower of the domain.", + responses = { + @ApiResponse( + responseCode = "200", + description = "OK", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = ChangeEvent.class))) + }) + public Response deleteFollower( + @Context SecurityContext securityContext, + @Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id") + UUID id, + @Parameter( + description = "Id of the user being removed as follower", + schema = @Schema(type = "string")) + @PathParam("userId") + String userId) { + return repository + .deleteFollower(securityContext.getUserPrincipal().getName(), id, UUID.fromString(userId)) + .toResponse(); + } } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DataProductResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DataProductResourceTest.java index dbbfed1b54a..ee260377036 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DataProductResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DataProductResourceTest.java @@ -308,7 +308,7 @@ public class DataProductResourceTest extends EntityResourceTest getDomain.getChildren(), getDomain.getOwners(), getDomain.getExperts()); - String fields = "children,owners,parent,experts,tags"; + String fields = "children,owners,parent,experts,tags,followers"; getDomain = byName ? getEntityByName(getDomain.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS) diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/domains/dataProduct.json b/openmetadata-spec/src/main/resources/json/schema/entity/domains/dataProduct.json index 65ae33d587e..69c3c046ba1 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/domains/dataProduct.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/domains/dataProduct.json @@ -83,6 +83,10 @@ "extension": { "description": "Entity extension data with custom attributes added to the entity.", "$ref": "../../type/basic.json#/definitions/entityExtension" + }, + "followers": { + "description": "Followers of this entity.", + "$ref": "../../type/entityReferenceList.json" } }, "required": ["id", "name", "description"], diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/domains/domain.json b/openmetadata-spec/src/main/resources/json/schema/entity/domains/domain.json index da03b06eb6b..3f506308a68 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/domains/domain.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/domains/domain.json @@ -102,6 +102,10 @@ "extension": { "description": "Entity extension data with custom attributes added to the entity.", "$ref": "../../type/basic.json#/definitions/entityExtension" + }, + "followers": { + "description": "Followers of this entity.", + "$ref": "../../type/entityReferenceList.json" } }, "required": ["id", "name", "description", "domainType"], diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/entity/domains/dataProduct.ts b/openmetadata-ui/src/main/resources/ui/src/generated/entity/domains/dataProduct.ts index 3a06076d932..a690dbe94b5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/entity/domains/dataProduct.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/entity/domains/dataProduct.ts @@ -44,6 +44,10 @@ export interface DataProduct { * Entity extension data with custom attributes added to the entity. */ extension?: any; + /** + * Followers of this entity. + */ + followers?: EntityReference[]; /** * FullyQualifiedName is `domain.dataProductName` or `sub-domain.dataProductName`. */ diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/entity/domains/domain.ts b/openmetadata-ui/src/main/resources/ui/src/generated/entity/domains/domain.ts index df8fa08f0ee..59169eb2bc3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/entity/domains/domain.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/entity/domains/domain.ts @@ -47,6 +47,10 @@ export interface Domain { * Entity extension data with custom attributes added to the entity. */ extension?: any; + /** + * Followers of this entity. + */ + followers?: EntityReference[]; /** * FullyQualifiedName same as `name`. */