From 9e34cb8440a751961ca019b4fe18198929cc8d89 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Sun, 13 Nov 2022 10:16:01 -0800 Subject: [PATCH] Fix #8416: Restore Entities after soft delete with all it's old data (#8635) --- .../service/jdbi3/EntityRepository.java | 3 +- .../service/resources/EntityResource.java | 8 +++ .../analytics/WebAnalyticEventResource.java | 21 ++++++++ .../service/resources/bots/BotResource.java | 20 +++++++ .../resources/charts/ChartResource.java | 20 +++++++ .../dashboards/DashboardResource.java | 20 +++++++ .../dataInsight/DataInsightChartResource.java | 21 ++++++++ .../resources/databases/DatabaseResource.java | 20 +++++++ .../databases/DatabaseSchemaResource.java | 20 +++++++ .../resources/databases/TableResource.java | 20 +++++++ .../resources/dqtests/TestCaseResource.java | 20 +++++++ .../dqtests/TestDefinitionResource.java | 20 +++++++ .../resources/dqtests/TestSuiteResource.java | 20 +++++++ .../resources/glossary/GlossaryResource.java | 20 +++++++ .../glossary/GlossaryTermResource.java | 20 +++++++ .../service/resources/kpi/KpiResource.java | 20 +++++++ .../resources/locations/LocationResource.java | 20 +++++++ .../resources/mlmodels/MlModelResource.java | 20 +++++++ .../resources/pipelines/PipelineResource.java | 20 +++++++ .../resources/policies/PolicyResource.java | 20 +++++++ .../dashboard/DashboardServiceResource.java | 21 ++++++++ .../database/DatabaseServiceResource.java | 37 ++++++++++--- .../IngestionPipelineResource.java | 53 +++++++++++++------ .../messaging/MessagingServiceResource.java | 37 ++++++++++--- .../mlmodel/MlModelServiceResource.java | 34 +++++++++--- .../pipeline/PipelineServiceResource.java | 37 ++++++++++--- .../storage/StorageServiceResource.java | 36 ++++++++++--- .../service/resources/teams/RoleResource.java | 20 +++++++ .../service/resources/teams/TeamResource.java | 20 +++++++ .../service/resources/teams/UserResource.java | 20 +++++++ .../resources/topics/TopicResource.java | 20 +++++++ .../service/resources/EntityResourceTest.java | 40 ++++++++++++-- .../openmetadata/service/util/TestUtils.java | 6 +++ .../json/schema/api/data/restoreEntity.json | 16 ++++++ 34 files changed, 710 insertions(+), 60 deletions(-) create mode 100644 openmetadata-spec/src/main/resources/json/schema/api/data/restoreEntity.json diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java index 52a29fca373..e58d0dec060 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java @@ -901,7 +901,7 @@ public abstract class EntityRepository { return RestUtil.getHref(uriInfo, collectionPath, id); } - public void restoreEntity(String updatedBy, String entityType, UUID id) throws IOException { + public T restoreEntity(String updatedBy, String entityType, UUID id) throws IOException { // If an entity being restored contains other **deleted** children entities, restore them List records = daoCollection.relationshipDAO().findTo(id.toString(), entityType, Relationship.CONTAINS.ordinal()); @@ -919,6 +919,7 @@ public abstract class EntityRepository { T entity = dao.findEntityById(id, DELETED); entity.setDeleted(false); dao.update(entity.getId(), JsonUtils.pojoToJson(entity)); + return entity; } public void addRelationship(UUID fromId, UUID toId, String fromEntity, String toEntity, Relationship relationship) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index fbcb8f5553f..2fe9355e03a 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -247,6 +247,14 @@ public abstract class EntityResource { return delete(uriInfo, securityContext, id, true, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted bot.", + tags = "bots", + description = "Restore a soft deleted bot.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Bot ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Bot.class))) + }) + public Response restoreBot( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + private Bot getBot(CreateBot create, String user) throws IOException { return copy(new Bot(), create, user) .withBotUser(create.getBotUser()) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java index 7ae877c13e3..1a3068e4bd4 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java @@ -45,6 +45,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import org.openmetadata.schema.api.data.CreateChart; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.entity.data.Chart; import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.Include; @@ -380,6 +381,25 @@ public class ChartResource extends EntityResource { return delete(uriInfo, securityContext, id, false, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted chart.", + tags = "charts", + description = "Restore a soft deleted chart.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Chart ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Chart.class))) + }) + public Response restoreChart( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + private Chart getChart(CreateChart create, String user) throws IOException { return copy(new Chart(), create, user) .withService(create.getService()) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java index 65ab228b73e..7c9ae4c7743 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java @@ -45,6 +45,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import org.openmetadata.schema.api.data.CreateDashboard; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.entity.data.Dashboard; import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.Include; @@ -382,6 +383,25 @@ public class DashboardResource extends EntityResource { return deleteByName(uriInfo, securityContext, fqn, false, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted table.", + tags = "tables", + description = "Restore a soft deleted table.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Table ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Table.class))) + }) + public Response restoreTable( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + @PUT @Path("/{id}/followers") @Operation( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java index eac33abbb42..9ddf1335f0e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java @@ -37,6 +37,7 @@ import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.tests.CreateTestCase; import org.openmetadata.schema.tests.TestCase; import org.openmetadata.schema.tests.type.TestCaseResult; @@ -425,6 +426,25 @@ public class TestCaseResource extends EntityResource { return delete(uriInfo, securityContext, id, recursive, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted Kpi.", + tags = "kpi", + description = "Restore a soft deleted Kpi.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Kpi. ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Kpi.class))) + }) + public Response restoreKpi( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + @PUT @Path("/{fqn}/kpiResult") @Operation( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/locations/LocationResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/locations/LocationResource.java index 2db58752565..770938998e3 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/locations/LocationResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/locations/LocationResource.java @@ -46,6 +46,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import org.openmetadata.schema.api.data.CreateLocation; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.entity.data.Location; import org.openmetadata.schema.type.ChangeEvent; import org.openmetadata.schema.type.EntityHistory; @@ -425,6 +426,25 @@ public class LocationResource extends EntityResource return delete(uriInfo, securityContext, id, false, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted MlModel.", + tags = "mlModels", + description = "Restore a soft deleted MlModel.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the MlModel ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = MlModel.class))) + }) + public Response restoreMlModel( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + private MlModel getMlModel(CreateMlModel create, String user) throws IOException { return copy(new MlModel(), create, user) .withService(create.getService()) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java index 59572ba580c..61df81190b4 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java @@ -46,6 +46,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import org.openmetadata.schema.api.data.CreatePipeline; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.entity.data.Pipeline; import org.openmetadata.schema.entity.data.PipelineStatus; import org.openmetadata.schema.type.ChangeEvent; @@ -497,6 +498,25 @@ public class PipelineResource extends EntityResource { return response; } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted policy.", + tags = "policies", + description = "Restore a soft deleted policy.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Policy ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Policy.class))) + }) + public Response restorePolicy( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + @GET @Path("/validation/condition/{expression}") @Operation( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java index 16b307b7e73..f3d5a53e24b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java @@ -43,6 +43,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.services.CreateDashboardService; import org.openmetadata.schema.entity.services.DashboardService; import org.openmetadata.schema.entity.services.ServiceType; @@ -344,6 +345,26 @@ public class DashboardServiceResource return delete(uriInfo, securityContext, id, recursive, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted DashboardService.", + tags = "dashboardServices", + description = "Restore a soft deleted DashboardService.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Chart ", + content = + @Content(mediaType = "application/json", schema = @Schema(implementation = DashboardService.class))) + }) + public Response restoreDashboardService( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + private DashboardService getService(CreateDashboardService create, String user) throws IOException { return copy(new DashboardService(), create, user) .withServiceType(create.getServiceType()) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java index 8d5a1646c50..30926f37f51 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java @@ -42,6 +42,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import lombok.extern.slf4j.Slf4j; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.services.CreateDatabaseService; import org.openmetadata.schema.api.services.DatabaseConnection; import org.openmetadata.schema.entity.services.DatabaseService; @@ -92,7 +93,7 @@ public class DatabaseServiceResource @Operation( operationId = "listDatabaseServices", summary = "List database services", - tags = "databaseService", + tags = "databaseServices", description = "Get a list of database services.", responses = { @ApiResponse( @@ -143,7 +144,7 @@ public class DatabaseServiceResource @Operation( operationId = "getDatabaseServiceByID", summary = "Get a database service", - tags = "databaseService", + tags = "databaseServices", description = "Get a database service by `id`.", responses = { @ApiResponse( @@ -178,7 +179,7 @@ public class DatabaseServiceResource @Operation( operationId = "getDatabaseServiceByFQN", summary = "Get database service by name", - tags = "databaseService", + tags = "databaseServices", description = "Get a database service by the service `name`.", responses = { @ApiResponse( @@ -213,7 +214,7 @@ public class DatabaseServiceResource @Operation( operationId = "listAllDatabaseServiceVersion", summary = "List database service versions", - tags = "databaseService", + tags = "databaseServices", description = "Get a list of all the versions of a database service identified by `id`", responses = { @ApiResponse( @@ -249,7 +250,7 @@ public class DatabaseServiceResource @Operation( operationId = "getSpecificDatabaseServiceVersion", summary = "Get a version of the database service", - tags = "databaseService", + tags = "databaseServices", description = "Get a version of the database service by given `id`", responses = { @ApiResponse( @@ -279,7 +280,7 @@ public class DatabaseServiceResource @Operation( operationId = "createDatabaseService", summary = "Create database service", - tags = "databaseService", + tags = "databaseServices", description = "Create a new database service.", responses = { @ApiResponse( @@ -302,7 +303,7 @@ public class DatabaseServiceResource @Operation( operationId = "createOrUpdateDatabaseService", summary = "Update database service", - tags = "databaseService", + tags = "databaseServices", description = "Update an existing or create a new database service.", responses = { @ApiResponse( @@ -326,7 +327,7 @@ public class DatabaseServiceResource @Operation( operationId = "deleteDatabaseService", summary = "Delete a database service", - tags = "databaseService", + tags = "databaseServices", description = "Delete a database services. If databases (and tables) belong the service, it can't be " + "deleted.", responses = { @@ -350,6 +351,26 @@ public class DatabaseServiceResource return delete(uriInfo, securityContext, id, recursive, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted DatabaseService.", + tags = "databaseServices", + description = "Restore a soft deleted DatabaseService.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the DatabaseService.", + content = + @Content(mediaType = "application/json", schema = @Schema(implementation = DatabaseService.class))) + }) + public Response restoreDatabaseService( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + private DatabaseService getService(CreateDatabaseService create, String user) throws IOException { return copy(new DatabaseService(), create, user) .withServiceType(create.getServiceType()) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java index 6fab67038b1..4f3643d9390 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java @@ -52,6 +52,7 @@ import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.services.ingestionPipelines.CreateIngestionPipeline; import org.openmetadata.schema.api.services.ingestionPipelines.TestServiceConnection; import org.openmetadata.schema.entity.services.ingestionPipelines.IngestionPipeline; @@ -120,7 +121,7 @@ public class IngestionPipelineResource extends EntityResource { return response; } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted role.", + tags = "roles", + description = "Restore a soft deleted role.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Role. ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Role.class))) + }) + public Response restoreRole( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + private Role getRole(CreateRole create, String user) throws IOException { if (nullOrEmpty(create.getPolicies())) { throw new IllegalArgumentException("At least one policy is required to create a role"); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java index 9987f0d3117..4f1a7bb930a 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java @@ -48,6 +48,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import lombok.extern.slf4j.Slf4j; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.teams.CreateTeam; import org.openmetadata.schema.api.teams.CreateTeam.TeamType; import org.openmetadata.schema.entity.teams.Team; @@ -408,6 +409,25 @@ public class TeamResource extends EntityResource { return delete(uriInfo, securityContext, id, recursive, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted Team.", + tags = "teams", + description = "Restore a soft deleted Team.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Team ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Team.class))) + }) + public Response restoreTeam( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + private Team getTeam(CreateTeam ct, String user) throws IOException { if (ct.getTeamType().equals(TeamType.ORGANIZATION)) { throw new IllegalArgumentException(CREATE_ORGANIZATION); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java index 5af06d7f8a1..ff9e7a285fc 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java @@ -67,6 +67,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import org.openmetadata.schema.EntityInterface; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.security.AuthenticationConfiguration; import org.openmetadata.schema.api.teams.CreateUser; import org.openmetadata.schema.auth.ChangePasswordRequest; @@ -743,6 +744,25 @@ public class UserResource extends EntityResource { return response; } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted User.", + tags = "users", + description = "Restore a soft deleted User.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the User ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = User.class))) + }) + public Response restoreTable( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + @POST @Path("/signup") @Operation( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java index c65510620e3..b57e80b1252 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java @@ -46,6 +46,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import org.openmetadata.schema.api.data.CreateTopic; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.entity.data.Topic; import org.openmetadata.schema.type.ChangeEvent; import org.openmetadata.schema.type.EntityHistory; @@ -418,6 +419,25 @@ public class TopicResource extends EntityResource { return delete(uriInfo, securityContext, id, false, hardDelete); } + @PUT + @Path("/restore") + @Operation( + operationId = "restore", + summary = "Restore a soft deleted topic.", + tags = "topics", + description = "Restore a soft deleted topic.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully restored the Topic. ", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Topic.class))) + }) + public Response restoreTopic( + @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid RestoreEntity restore) + throws IOException { + return restoreEntity(uriInfo, securityContext, restore.getId()); + } + private Topic getTopic(CreateTopic create, String user) throws IOException { return copy(new Topic(), create, user) .withService(create.getService()) diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java index 97271a003df..80d3fdbee6e 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java @@ -98,6 +98,7 @@ import org.junit.jupiter.api.TestInstance; import org.openmetadata.common.utils.CommonUtil; import org.openmetadata.schema.CreateEntity; import org.openmetadata.schema.EntityInterface; +import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.TermReference; import org.openmetadata.schema.api.teams.CreateTeam; import org.openmetadata.schema.api.teams.CreateTeam.TeamType; @@ -1439,9 +1440,9 @@ public abstract class EntityResourceTest authHeaders) + throws HttpResponseException { + WebTarget target = getRestoreResource(restore.getId()); + return TestUtils.put(target, restore, entityClass, status, authHeaders); + } + /** Helper function to create an entity, submit POST API request and validate response. */ public final T createAndCheckEntity(K create, Map authHeaders) throws IOException { // Validate an entity that is created has all the information set in create request @@ -1723,6 +1734,27 @@ public abstract class EntityResourceTest authHeaders, + UpdateType updateType, + ChangeDescription changeDescription) + throws IOException { + T updated = restoreEntity(new RestoreEntity().withId(entity.getId()), status, authHeaders); + validateLatestVersion(updated, updateType, changeDescription, authHeaders); + // GET the newly updated entity and validate + T getEntity = getEntity(updated.getId(), authHeaders); + validateChangeDescription(getEntity, updateType, changeDescription); + // Check if the entity change events are record + if (updateType != NO_CHANGE) { + EventType expectedEventType = + updateType == UpdateType.CREATED ? EventType.ENTITY_CREATED : EventType.ENTITY_UPDATED; + validateChangeEvents(updated, updated.getUpdatedAt(), expectedEventType, changeDescription, authHeaders); + } + return updated; + } + protected void validateEntityHistory( UUID id, UpdateType updateType, ChangeDescription expectedChangeDescription, Map authHeaders) throws IOException { diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/util/TestUtils.java b/openmetadata-service/src/test/java/org/openmetadata/service/util/TestUtils.java index cfd88e41590..26ce8158715 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/util/TestUtils.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/util/TestUtils.java @@ -323,6 +323,12 @@ public final class TestUtils { return readResponse(response, clz, Status.OK.getStatusCode()); } + public static T put(WebTarget target, Class clz, Status expectedStatus, Map headers) + throws HttpResponseException { + Response response = SecurityUtil.addHeaders(target, headers).method("PUT"); + return readResponse(response, clz, expectedStatus.getStatusCode()); + } + public static void put(WebTarget target, K request, Status expectedStatus, Map headers) throws HttpResponseException { Response response = diff --git a/openmetadata-spec/src/main/resources/json/schema/api/data/restoreEntity.json b/openmetadata-spec/src/main/resources/json/schema/api/data/restoreEntity.json new file mode 100644 index 00000000000..51fbe29ed64 --- /dev/null +++ b/openmetadata-spec/src/main/resources/json/schema/api/data/restoreEntity.json @@ -0,0 +1,16 @@ +{ + "$id": "https://open-metadata.org/schema/api/restoreEntity.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "restoreEntity", + "description": "Restore Entity API request to restore soft deleted entity.", + "type": "object", + "javaType": "org.openmetadata.schema.api.data.RestoreEntity", + "properties": { + "id": { + "description": "Unique identifier that identifies an entity instance.", + "$ref": "../../type/basic.json#/definitions/uuid" + } + }, + "required": ["id"], + "additionalProperties": false +}