Adds followers to services and database schema (#21017)

* Adds followers to services and database schema

* Add delete and followers fields

* Add missing fields

* Fix failing test for get entity with different fields

* Fix ingestion test case failure
This commit is contained in:
Mohit Yadav 2025-04-30 17:15:13 +05:30 committed by GitHub
parent 7cd798b004
commit 8314fdf8d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 833 additions and 37 deletions

View File

@ -77,7 +77,8 @@ public class DatabaseSchemaResource
extends EntityResource<DatabaseSchema, DatabaseSchemaRepository> {
private final DatabaseSchemaMapper mapper = new DatabaseSchemaMapper();
public static final String COLLECTION_PATH = "v1/databaseSchemas/";
static final String FIELDS = "owners,tables,usageSummary,tags,extension,domain,sourceHash";
static final String FIELDS =
"owners,tables,usageSummary,tags,extension,domain,sourceHash,followers";
@Override
public DatabaseSchema addHref(UriInfo uriInfo, DatabaseSchema schema) {
@ -315,6 +316,69 @@ public class DatabaseSchemaResource
return create(uriInfo, securityContext, schema);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToDatabaseSchema",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this Database Schema",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Dashboard Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Database Schema", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@PATCH
@Path("/{id}")
@Operation(

View File

@ -1294,7 +1294,7 @@ public class TableResource extends EntityResource<Table, TableRepository> {
@Operation(
operationId = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the table.",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
@ -1307,7 +1307,7 @@ public class TableResource extends EntityResource<Table, TableRepository> {
public Response deleteFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the table", schema = @Schema(type = "UUID")) @PathParam("id")
@Parameter(description = "Id of the Entity", schema = @Schema(type = "UUID")) @PathParam("id")
UUID id,
@Parameter(
description = "Id of the user being removed as follower",

View File

@ -52,6 +52,7 @@ import org.openmetadata.schema.entity.services.ApiService;
import org.openmetadata.schema.entity.services.ServiceType;
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
import org.openmetadata.schema.type.ApiConnection;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
@ -78,7 +79,7 @@ public class APIServiceResource
extends ServiceEntityResource<ApiService, APIServiceRepository, ApiConnection> {
private final APIServiceMapper mapper = new APIServiceMapper();
public static final String COLLECTION_PATH = "v1/services/apiServices/";
static final String FIELDS = "pipelines,owners,tags,domain";
public static final String FIELDS = "pipelines,owners,tags,domain,followers";
@Override
public ApiService addHref(UriInfo uriInfo, ApiService service) {
@ -524,6 +525,69 @@ public class APIServiceResource
uriInfo, securityContext, EntityInterfaceUtil.quoteName(fqn), recursive, hardDelete);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToApiService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this api service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Api Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Api Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@PUT
@Path("/restore")
@Operation(

View File

@ -50,6 +50,7 @@ import org.openmetadata.schema.api.services.CreateDashboardService;
import org.openmetadata.schema.entity.services.DashboardService;
import org.openmetadata.schema.entity.services.ServiceType;
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.DashboardConnection;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
@ -74,7 +75,7 @@ public class DashboardServiceResource
DashboardService, DashboardServiceRepository, DashboardConnection> {
private final DashboardServiceMapper mapper = new DashboardServiceMapper();
public static final String COLLECTION_PATH = "v1/services/dashboardServices";
static final String FIELDS = "owners,domain";
public static final String FIELDS = "owners,domain,followers";
public DashboardServiceResource(Authorizer authorizer, Limits limits) {
super(Entity.DASHBOARD_SERVICE, authorizer, limits, ServiceType.DASHBOARD);
@ -173,6 +174,69 @@ public class DashboardServiceResource
return decryptOrNullify(securityContext, dashboardService);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToDashboardService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this Dashboard service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Dashboard Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Dashboard Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@GET
@Path("/name/{name}")
@Operation(

View File

@ -53,6 +53,7 @@ import org.openmetadata.schema.api.services.DatabaseConnection;
import org.openmetadata.schema.entity.services.DatabaseService;
import org.openmetadata.schema.entity.services.ServiceType;
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
@ -82,7 +83,7 @@ public class DatabaseServiceResource
extends ServiceEntityResource<DatabaseService, DatabaseServiceRepository, DatabaseConnection> {
private final DatabaseServiceMapper mapper = new DatabaseServiceMapper();
public static final String COLLECTION_PATH = "v1/services/databaseServices/";
static final String FIELDS = "pipelines,owners,tags,domain";
public static final String FIELDS = "pipelines,owners,tags,domain,followers";
@Override
public DatabaseService addHref(UriInfo uriInfo, DatabaseService service) {
@ -386,6 +387,69 @@ public class DatabaseServiceResource
return response;
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToDatabaseService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this database service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Database Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Database Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@PATCH
@Path("/{id}")
@Operation(

View File

@ -63,6 +63,7 @@ import org.openmetadata.schema.entity.services.ingestionPipelines.IngestionPipel
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineServiceClientResponse;
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineStatus;
import org.openmetadata.schema.services.connections.metadata.OpenMetadataConnection;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
@ -101,7 +102,7 @@ public class IngestionPipelineResource
public static final String COLLECTION_PATH = "v1/services/ingestionPipelines/";
private PipelineServiceClientInterface pipelineServiceClient;
private OpenMetadataApplicationConfig openMetadataApplicationConfig;
static final String FIELDS = FIELD_OWNERS;
static final String FIELDS = "owners,followers";
@Override
public IngestionPipeline addHref(UriInfo uriInfo, IngestionPipeline ingestionPipeline) {
@ -259,6 +260,69 @@ public class IngestionPipelineResource
return ingestionPipelines;
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToIngestionPipeline",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this ingestion pipeline",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Ingestion Pipeline for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Ingestion Pipeline", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@GET
@Path("/{id}/versions")
@Operation(

View File

@ -50,6 +50,7 @@ import org.openmetadata.schema.api.services.CreateMessagingService;
import org.openmetadata.schema.entity.services.MessagingService;
import org.openmetadata.schema.entity.services.ServiceType;
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MessagingConnection;
@ -74,7 +75,7 @@ public class MessagingServiceResource
MessagingService, MessagingServiceRepository, MessagingConnection> {
private final MessagingServiceMapper mapper = new MessagingServiceMapper();
public static final String COLLECTION_PATH = "v1/services/messagingServices/";
public static final String FIELDS = "owners,domain";
public static final String FIELDS = "owners,domain,followers";
public MessagingServiceResource(Authorizer authorizer, Limits limits) {
super(Entity.MESSAGING_SERVICE, authorizer, limits, ServiceType.MESSAGING);
@ -247,6 +248,69 @@ public class MessagingServiceResource
return decryptOrNullify(securityContext, service);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToDatabaseService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this messaging service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Messaging Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Messaging Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@GET
@Path("/{id}/versions")
@Operation(

View File

@ -47,6 +47,7 @@ import org.openmetadata.schema.service.configuration.elasticsearch.ElasticSearch
import org.openmetadata.schema.services.connections.metadata.ComponentConfig;
import org.openmetadata.schema.services.connections.metadata.ElasticsSearch;
import org.openmetadata.schema.services.connections.metadata.OpenMetadataConnection;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
@ -77,7 +78,7 @@ public class MetadataServiceResource
private final MetadataServiceMapper mapper = new MetadataServiceMapper();
public static final String OPENMETADATA_SERVICE = "OpenMetadata";
public static final String COLLECTION_PATH = "v1/services/metadataServices/";
public static final String FIELDS = "pipelines,owners,tags";
public static final String FIELDS = "pipelines,owners,tags,followers";
@Override
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
@ -257,6 +258,69 @@ public class MetadataServiceResource
return decryptOrNullify(securityContext, metadataService);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToMetadataService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this Metadata service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Metadata Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Metadata Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@PUT
@Path("/{id}/testConnectionResult")
@Operation(

View File

@ -52,6 +52,7 @@ import org.openmetadata.schema.api.services.CreateMlModelService;
import org.openmetadata.schema.entity.services.MlModelService;
import org.openmetadata.schema.entity.services.ServiceType;
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
@ -75,7 +76,7 @@ public class MlModelServiceResource
extends ServiceEntityResource<MlModelService, MlModelServiceRepository, MlModelConnection> {
private final MlModelServiceMapper mapper = new MlModelServiceMapper();
public static final String COLLECTION_PATH = "v1/services/mlmodelServices/";
public static final String FIELDS = "pipelines,owners,tags,domain";
public static final String FIELDS = "pipelines,owners,tags,domain,followers";
@Override
public MlModelService addHref(UriInfo uriInfo, MlModelService service) {
@ -232,6 +233,69 @@ public class MlModelServiceResource
return decryptOrNullify(securityContext, mlModelService);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToMlModelService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this MlModel service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "MlModel Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the MlModel Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@PUT
@Path("/{id}/testConnectionResult")
@Operation(

View File

@ -50,6 +50,7 @@ import org.openmetadata.schema.api.services.CreatePipelineService;
import org.openmetadata.schema.entity.services.PipelineService;
import org.openmetadata.schema.entity.services.ServiceType;
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
@ -73,7 +74,7 @@ public class PipelineServiceResource
extends ServiceEntityResource<PipelineService, PipelineServiceRepository, PipelineConnection> {
private final PipelineServiceMapper mapper = new PipelineServiceMapper();
public static final String COLLECTION_PATH = "v1/services/pipelineServices/";
static final String FIELDS = "pipelines,owners,domain";
public static final String FIELDS = "pipelines,owners,domain,followers";
@Override
public PipelineService addHref(UriInfo uriInfo, PipelineService service) {
@ -233,6 +234,69 @@ public class PipelineServiceResource
return decryptOrNullify(securityContext, pipelineService);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToDatabaseService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this pipeline service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Pipeline Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Pipeline Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@PUT
@Path("/{id}/testConnectionResult")
@Operation(

View File

@ -38,6 +38,7 @@ import org.openmetadata.schema.api.services.CreateSearchService;
import org.openmetadata.schema.entity.services.SearchService;
import org.openmetadata.schema.entity.services.ServiceType;
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
@ -65,7 +66,7 @@ public class SearchServiceResource
extends ServiceEntityResource<SearchService, SearchServiceRepository, SearchConnection> {
private final SearchServiceMapper mapper = new SearchServiceMapper();
public static final String COLLECTION_PATH = "v1/services/searchServices/";
static final String FIELDS = "pipelines,owners,tags,domain";
public static final String FIELDS = "pipelines,owners,tags,domain,followers";
@Override
public SearchService addHref(UriInfo uriInfo, SearchService service) {
@ -241,6 +242,69 @@ public class SearchServiceResource
return decryptOrNullify(securityContext, service);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToDatabaseService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this Search Index service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Search Index Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Search Index Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@GET
@Path("/{id}/versions")
@Operation(

View File

@ -39,6 +39,7 @@ import org.openmetadata.schema.entity.services.DatabaseService;
import org.openmetadata.schema.entity.services.ServiceType;
import org.openmetadata.schema.entity.services.StorageService;
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
@ -65,7 +66,7 @@ public class StorageServiceResource
extends ServiceEntityResource<StorageService, StorageServiceRepository, StorageConnection> {
private final StorageServiceMapper mapper = new StorageServiceMapper();
public static final String COLLECTION_PATH = "v1/services/storageServices/";
static final String FIELDS = "pipelines,owners,tags,domain";
public static final String FIELDS = "pipelines,owners,tags,domain,followers";
@Override
public StorageService addHref(UriInfo uriInfo, StorageService service) {
@ -212,6 +213,69 @@ public class StorageServiceResource
return decryptOrNullify(securityContext, storageService);
}
@PUT
@Path("/{id}/followers")
@Operation(
operationId = "addFollowerToDatabaseService",
summary = "Add a follower",
description = "Add a user identified by `userId` as followed of this Storage service",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(
responseCode = "404",
description = "Storage Service for instance {id} is not found")
})
public Response addFollower(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Id of the Storage Service", 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 = "deleteFollower",
summary = "Remove a follower",
description = "Remove the user identified `userId` as a follower of the entity.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ChangeEvent.class)))
})
public Response deleteFollower(
@Context UriInfo uriInfo,
@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();
}
@PUT
@Path("/{id}/testConnectionResult")
@Operation(

View File

@ -262,7 +262,7 @@ public class DatabaseSchemaResourceTest
assertListNotNull(schema.getService(), schema.getServiceType(), schema.getDatabase());
assertListNull(schema.getOwners(), schema.getTables());
fields = "owners,tags,tables";
fields = "owners,tags,tables,followers";
schema =
byName
? getEntityByName(schema.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -41,7 +41,7 @@ public class APIServiceResourceTest extends ServiceResourceTest<ApiService, Crea
ApiService.class,
APIServiceResource.APIServiceList.class,
"services/apiServices",
"owners");
APIServiceResource.FIELDS);
this.supportsPatch = false;
}
@ -190,7 +190,7 @@ public class APIServiceResourceTest extends ServiceResourceTest<ApiService, Crea
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -55,6 +55,7 @@ import org.openmetadata.schema.type.DashboardConnection;
import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.charts.ChartResourceTest;
import org.openmetadata.service.resources.dashboards.DashboardResourceTest;
import org.openmetadata.service.resources.services.dashboard.DashboardServiceResource;
import org.openmetadata.service.resources.services.dashboard.DashboardServiceResource.DashboardServiceList;
import org.openmetadata.service.secrets.masker.PasswordEntityMasker;
import org.openmetadata.service.util.JsonUtils;
@ -69,7 +70,7 @@ public class DashboardServiceResourceTest
DashboardService.class,
DashboardServiceList.class,
"services/dashboardServices",
"owners");
DashboardServiceResource.FIELDS);
this.supportsPatch = false;
}
@ -247,7 +248,7 @@ public class DashboardServiceResourceTest
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -82,6 +82,7 @@ import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.databases.DatabaseResourceTest;
import org.openmetadata.service.resources.databases.DatabaseSchemaResourceTest;
import org.openmetadata.service.resources.databases.TableResourceTest;
import org.openmetadata.service.resources.services.database.DatabaseServiceResource;
import org.openmetadata.service.resources.services.database.DatabaseServiceResource.DatabaseServiceList;
import org.openmetadata.service.resources.services.ingestionpipelines.IngestionPipelineResourceTest;
import org.openmetadata.service.secrets.masker.PasswordEntityMasker;
@ -98,7 +99,7 @@ public class DatabaseServiceResourceTest
DatabaseService.class,
DatabaseServiceList.class,
"services/databaseServices",
"owners,tags");
DatabaseServiceResource.FIELDS);
this.supportsPatch = false;
}
@ -537,7 +538,7 @@ public class DatabaseServiceResourceTest
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -242,7 +242,7 @@ public class MessagingServiceResourceTest
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), null, fields, ADMIN_AUTH_HEADERS)

View File

@ -234,7 +234,7 @@ public class MetadataServiceResourceTest
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), null, fields, ADMIN_AUTH_HEADERS)

View File

@ -42,6 +42,7 @@ import org.openmetadata.schema.services.connections.mlmodel.MlflowConnection;
import org.openmetadata.schema.type.ChangeDescription;
import org.openmetadata.schema.type.MlModelConnection;
import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.services.mlmodel.MlModelServiceResource;
import org.openmetadata.service.resources.services.mlmodel.MlModelServiceResource.MlModelServiceList;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.TestUtils;
@ -55,7 +56,7 @@ public class MlModelServiceResourceTest
MlModelService.class,
MlModelServiceList.class,
"services/mlmodelServices",
"owners");
MlModelServiceResource.FIELDS);
this.supportsPatch = false;
supportsSearchIndex = true;
}
@ -200,7 +201,7 @@ public class MlModelServiceResourceTest
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -58,6 +58,7 @@ import org.openmetadata.schema.type.ChangeDescription;
import org.openmetadata.schema.type.PipelineConnection;
import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.services.ingestionpipelines.IngestionPipelineResourceTest;
import org.openmetadata.service.resources.services.pipeline.PipelineServiceResource;
import org.openmetadata.service.resources.services.pipeline.PipelineServiceResource.PipelineServiceList;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.TestUtils;
@ -71,7 +72,7 @@ public class PipelineServiceResourceTest
PipelineService.class,
PipelineServiceList.class,
"services/pipelineServices",
"owners");
PipelineServiceResource.FIELDS);
this.supportsPatch = false;
}
@ -261,7 +262,7 @@ public class PipelineServiceResourceTest
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -42,7 +42,7 @@ public class SearchServiceResourceTest
SearchService.class,
SearchServiceResource.SearchServiceList.class,
"services/searchServices",
"owners");
SearchServiceResource.FIELDS);
this.supportsPatch = false;
}
@ -186,7 +186,7 @@ public class SearchServiceResourceTest
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -40,7 +40,7 @@ public class StorageServiceResourceTest
StorageService.class,
StorageServiceResource.StorageServiceList.class,
"services/storageServices",
"owners");
StorageServiceResource.FIELDS);
this.supportsPatch = false;
}
@ -186,7 +186,7 @@ public class StorageServiceResourceTest
: getEntity(service.getId(), fields, ADMIN_AUTH_HEADERS);
TestUtils.assertListNull(service.getOwners());
fields = "owners,tags";
fields = "owners,tags,followers";
service =
byName
? getEntityByName(service.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -882,7 +882,7 @@ public class IngestionPipelineResourceTest
assertListNotNull(ingestion.getService());
assertListNull(ingestion.getOwners());
fields = FIELD_OWNERS;
fields = "owners,followers";
ingestion =
byName
? getEntityByName(ingestion.getFullyQualifiedName(), fields, ADMIN_AUTH_HEADERS)

View File

@ -121,6 +121,10 @@
"description": "Life Cycle properties of the entity",
"$ref": "../../type/lifeCycle.json"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"certification": {
"$ref": "../../type/assetCertification.json"
},

View File

@ -93,6 +93,10 @@
},
"default": []
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"version": {
"description": "Metadata version of the entity.",
"$ref": "../../type/entityHistory.json#/definitions/entityVersion"

View File

@ -233,6 +233,10 @@
"description": "Domain the Dashboard service belongs to.",
"$ref": "../../type/entityReference.json"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"ingestionRunner" : {
"description": "The ingestion agent responsible for executing the ingestion pipeline.",
"$ref": "../../type/entityReference.json"

View File

@ -415,6 +415,10 @@
"description": "Link to the resource corresponding to this database service.",
"$ref": "../../type/basic.json#/definitions/href"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"changeDescription": {
"description": "Change that lead to this version of the entity.",
"$ref": "../../type/entityHistory.json#/definitions/changeDescription"

View File

@ -215,6 +215,10 @@
"description": "Change that lead to this version of the entity.",
"$ref": "../../../type/entityHistory.json#/definitions/changeDescription"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../../type/entityReferenceList.json"
},
"deleted": {
"description": "When `true` indicates the entity has been soft deleted.",
"type": "boolean",

View File

@ -152,6 +152,10 @@
"description": "Domain the Messaging service belongs to.",
"$ref": "../../type/entityReference.json"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"ingestionRunner" : {
"description": "The ingestion agent responsible for executing the ingestion pipeline.",
"$ref": "../../type/entityReference.json"

View File

@ -151,6 +151,10 @@
"provider" : {
"$ref": "../../type/basic.json#/definitions/providerType"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"domain" : {
"description": "Domain the asset belongs to. When not set, the asset inherits the domain from the parent it belongs to.",
"$ref": "../../type/entityReference.json"

View File

@ -146,6 +146,10 @@
"description": "List of data products this entity is part of.",
"$ref" : "../../type/entityReferenceList.json"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"domain" : {
"description": "Domain the MLModel service belongs to.",
"$ref": "../../type/entityReference.json"

View File

@ -252,6 +252,10 @@
"description": "List of data products this entity is part of.",
"$ref" : "../../type/entityReferenceList.json"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"domain" : {
"description": "Domain the Pipeline service belongs to.",
"$ref": "../../type/entityReference.json"

View File

@ -140,6 +140,10 @@
"description": "List of data products this entity is part of.",
"$ref" : "../../type/entityReferenceList.json"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"domain" : {
"description": "Domain the search service belongs to.",
"$ref": "../../type/entityReference.json"

View File

@ -147,6 +147,10 @@
"description": "List of data products this entity is part of.",
"$ref" : "../../type/entityReferenceList.json"
},
"followers": {
"description": "Followers of this entity.",
"$ref": "../../type/entityReferenceList.json"
},
"domain" : {
"description": "Domain the Storage service belongs to.",
"$ref": "../../type/entityReference.json"

View File

@ -53,6 +53,10 @@ export interface DatabaseSchema {
* Entity extension data with custom attributes added to the entity.
*/
extension?: any;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* Name that uniquely identifies a schema in the format
* 'ServiceName.DatabaseName.SchemaName'.

View File

@ -40,6 +40,10 @@ export interface APIService {
* Domain the API service belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/

View File

@ -39,6 +39,10 @@ export interface DashboardService {
* Domain the Dashboard service belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/

View File

@ -41,6 +41,10 @@ export interface DatabaseService {
* Domain the Database service belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/

View File

@ -49,6 +49,10 @@ export interface IngestionPipeline {
* True if the pipeline is ready to be run in the next schedule. False if it is paused.
*/
enabled?: boolean;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* Name that uniquely identifies a Pipeline.
*/
@ -251,15 +255,15 @@ export interface FieldChange {
* example, a table has an attribute called database of type EntityReference that captures
* the relationship of a table `belongs to a` database.
*
* The ingestion agent responsible for executing the ingestion pipeline.
*
* Owners of this Pipeline.
* Followers of this entity.
*
* This schema defines the EntityReferenceList type used for referencing an entity.
* EntityReference is used for capturing relationships from one entity to another. For
* example, a table has an attribute called database of type EntityReference that captures
* the relationship of a table `belongs to a` database.
*
* The ingestion agent responsible for executing the ingestion pipeline.
*
* Link to the service (such as database, messaging, storage services, etc. for which this
* ingestion pipeline ingests the metadata from.
*

View File

@ -40,6 +40,10 @@ export interface MessagingService {
* Domain the Messaging service belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/

View File

@ -36,6 +36,10 @@ export interface MetadataService {
* it belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/
@ -924,14 +928,14 @@ export enum VerifySSL {
* example, a table has an attribute called database of type EntityReference that captures
* the relationship of a table `belongs to a` database.
*
* The ingestion agent responsible for executing the ingestion pipeline.
*
* Owners of this database service.
* Followers of this entity.
*
* This schema defines the EntityReferenceList type used for referencing an entity.
* EntityReference is used for capturing relationships from one entity to another. For
* example, a table has an attribute called database of type EntityReference that captures
* the relationship of a table `belongs to a` database.
*
* The ingestion agent responsible for executing the ingestion pipeline.
*/
export interface EntityReference {
/**

View File

@ -40,6 +40,10 @@ export interface MlmodelService {
* Domain the MLModel service belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/

View File

@ -40,6 +40,10 @@ export interface PipelineService {
* Domain the Pipeline service belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/

View File

@ -39,6 +39,10 @@ export interface SearchService {
* Domain the search service belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/

View File

@ -39,6 +39,10 @@ export interface StorageService {
* Domain the Storage service belongs to.
*/
domain?: EntityReference;
/**
* Followers of this entity.
*/
followers?: EntityReference[];
/**
* FullyQualifiedName same as `name`.
*/