Support followers in Domain and DataProduct (#21478)

* Support followers in DataProduct

* Support followers in domain
This commit is contained in:
sonika-shah 2025-06-05 11:17:13 +05:30 committed by GitHub
parent f0f1debb6d
commit cfd548556e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 139 additions and 4 deletions

View File

@ -78,7 +78,7 @@ import org.openmetadata.service.util.ResultList;
public class DataProductResource extends EntityResource<DataProduct, DataProductRepository> {
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<DataProduct, DataProduct
String name) {
return deleteByName(uriInfo, securityContext, name, true, true);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToDataProduct",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this DataProduct",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "DataProduct for instance {id} is not found")
})
public Response addFollower(
@Context SecurityContext securityContext,
@Parameter(description = "Id of the DataProduct", 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 = "deleteFollowerFromDataProduct",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the dataProduct.",
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();
}
}

View File

@ -75,7 +75,7 @@ import org.openmetadata.service.util.ResultList;
public class DomainResource extends EntityResource<Domain, DomainRepository> {
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<Domain, DomainRepository> {
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();
}
}

View File

@ -308,7 +308,7 @@ public class DataProductResourceTest extends EntityResourceTest<DataProduct, Cre
? getEntityByName(dataProduct.getFullyQualifiedName(), null, ADMIN_AUTH_HEADERS)
: getEntity(dataProduct.getId(), null, ADMIN_AUTH_HEADERS);
assertListNull(getDataProduct.getOwners(), getDataProduct.getExperts());
String fields = "owners,domain,experts,assets,tags";
String fields = "owners,domain,experts,assets,tags,followers";
getDataProduct =
byName
? getEntityByName(getDataProduct.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -406,7 +406,7 @@ public class DomainResourceTest extends EntityResourceTest<Domain, CreateDomain>
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)

View File

@ -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"],

View File

@ -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"],

View File

@ -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`.
*/

View File

@ -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`.
*/