MINOR: add the systemApp property (#15473)

* feat: add "systemApp" property

* use BAD_REQUEST for System Apps uninstall

* use IllegalArgumentException
This commit is contained in:
Imri Paran 2024-03-11 15:39:20 +01:00 committed by GitHub
parent 67c57ec4f0
commit 4bb5550a46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 52 additions and 3 deletions

View File

@ -442,7 +442,8 @@ public class AppMarketPlaceResource
.withAppScreenshots(create.getAppScreenshots()) .withAppScreenshots(create.getAppScreenshots())
.withFeatures(create.getFeatures()) .withFeatures(create.getFeatures())
.withSourcePythonClass(create.getSourcePythonClass()) .withSourcePythonClass(create.getSourcePythonClass())
.withAllowConfiguration(create.getAllowConfiguration()); .withAllowConfiguration(create.getAllowConfiguration())
.withSystemApp(create.getSystemApp());
// Validate App // Validate App
validateApplication(app); validateApplication(app);

View File

@ -66,6 +66,7 @@ import org.openmetadata.service.OpenMetadataApplicationConfig;
import org.openmetadata.service.apps.ApplicationHandler; import org.openmetadata.service.apps.ApplicationHandler;
import org.openmetadata.service.apps.scheduler.AppScheduler; import org.openmetadata.service.apps.scheduler.AppScheduler;
import org.openmetadata.service.clients.pipeline.PipelineServiceClientFactory; import org.openmetadata.service.clients.pipeline.PipelineServiceClientFactory;
import org.openmetadata.service.exception.CatalogExceptionMessage;
import org.openmetadata.service.exception.EntityNotFoundException; import org.openmetadata.service.exception.EntityNotFoundException;
import org.openmetadata.service.jdbi3.AppRepository; import org.openmetadata.service.jdbi3.AppRepository;
import org.openmetadata.service.jdbi3.CollectionDAO; import org.openmetadata.service.jdbi3.CollectionDAO;
@ -651,6 +652,9 @@ public class AppResource extends EntityResource<App, AppRepository> {
description = "Delete a App by `name`.", description = "Delete a App by `name`.",
responses = { responses = {
@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(
responseCode = "400",
description = "System entity {name} of type SystemApp can not be deleted."),
@ApiResponse(responseCode = "404", description = "App for instance {name} is not found") @ApiResponse(responseCode = "404", description = "App for instance {name} is not found")
}) })
public Response delete( public Response delete(
@ -664,6 +668,10 @@ public class AppResource extends EntityResource<App, AppRepository> {
@PathParam("name") @PathParam("name")
String name) { String name) {
App app = repository.getByName(null, name, repository.getFields("bot,pipelines")); App app = repository.getByName(null, name, repository.getFields("bot,pipelines"));
if (app.getSystemApp()) {
throw new IllegalArgumentException(
CatalogExceptionMessage.systemEntityDeleteNotAllowed(app.getName(), "SystemApp"));
}
// Remove from Pipeline Service // Remove from Pipeline Service
deleteApp(securityContext, app, hardDelete); deleteApp(securityContext, app, hardDelete);
return deleteByName(uriInfo, securityContext, name, true, hardDelete); return deleteByName(uriInfo, securityContext, name, true, hardDelete);
@ -677,6 +685,9 @@ public class AppResource extends EntityResource<App, AppRepository> {
description = "Delete a App by `Id`.", description = "Delete a App by `Id`.",
responses = { responses = {
@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(
responseCode = "400",
description = "System entity {name} of type SystemApp can not be deleted."),
@ApiResponse(responseCode = "404", description = "App for instance {id} is not found") @ApiResponse(responseCode = "404", description = "App for instance {id} is not found")
}) })
public Response delete( public Response delete(
@ -689,6 +700,10 @@ public class AppResource extends EntityResource<App, AppRepository> {
@Parameter(description = "Id of the App", schema = @Schema(type = "UUID")) @PathParam("id") @Parameter(description = "Id of the App", schema = @Schema(type = "UUID")) @PathParam("id")
UUID id) { UUID id) {
App app = repository.get(null, id, repository.getFields("bot,pipelines")); App app = repository.get(null, id, repository.getFields("bot,pipelines"));
if (app.getSystemApp()) {
throw new IllegalArgumentException(
CatalogExceptionMessage.systemEntityDeleteNotAllowed(app.getName(), "SystemApp"));
}
// Remove from Pipeline Service // Remove from Pipeline Service
deleteApp(securityContext, app, hardDelete); deleteApp(securityContext, app, hardDelete);
// Remove from repository // Remove from repository
@ -955,7 +970,8 @@ public class AppResource extends EntityResource<App, AppRepository> {
.withAppScreenshots(marketPlaceDefinition.getAppScreenshots()) .withAppScreenshots(marketPlaceDefinition.getAppScreenshots())
.withFeatures(marketPlaceDefinition.getFeatures()) .withFeatures(marketPlaceDefinition.getFeatures())
.withSourcePythonClass(marketPlaceDefinition.getSourcePythonClass()) .withSourcePythonClass(marketPlaceDefinition.getSourcePythonClass())
.withAllowConfiguration(marketPlaceDefinition.getAllowConfiguration()); .withAllowConfiguration(marketPlaceDefinition.getAllowConfiguration())
.withSystemApp(marketPlaceDefinition.getSystemApp());
// validate Bot if provided // validate Bot if provided
validateAndAddBot(app, createAppRequest.getBot()); validateAndAddBot(app, createAppRequest.getBot());

View File

@ -1,7 +1,10 @@
package org.openmetadata.service.resources.apps; package org.openmetadata.service.resources.apps;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS; import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS;
import static org.openmetadata.service.util.TestUtils.assertResponseContains;
import java.io.IOException;
import java.util.Map; import java.util.Map;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -19,6 +22,7 @@ import org.openmetadata.service.util.TestUtils;
@Slf4j @Slf4j
public class AppsResourceTest extends EntityResourceTest<App, CreateApp> { public class AppsResourceTest extends EntityResourceTest<App, CreateApp> {
private static final String SYSTEM_APP_NAME = "systemApp";
public AppsResourceTest() { public AppsResourceTest() {
super(Entity.APPLICATION, App.class, AppResource.AppList.class, "apps", AppResource.FIELDS); super(Entity.APPLICATION, App.class, AppResource.AppList.class, "apps", AppResource.FIELDS);
@ -36,7 +40,10 @@ public class AppsResourceTest extends EntityResourceTest<App, CreateApp> {
appMarketPlaceDefinition = appMarketPlaceDefinition =
appMarketPlaceResourceTest.getEntityByName(name, ADMIN_AUTH_HEADERS); appMarketPlaceResourceTest.getEntityByName(name, ADMIN_AUTH_HEADERS);
} catch (EntityNotFoundException | HttpResponseException ex) { } catch (EntityNotFoundException | HttpResponseException ex) {
CreateAppMarketPlaceDefinitionReq req = appMarketPlaceResourceTest.createRequest(name); CreateAppMarketPlaceDefinitionReq req =
appMarketPlaceResourceTest
.createRequest(name)
.withSystemApp(name.equals(SYSTEM_APP_NAME));
appMarketPlaceDefinition = appMarketPlaceDefinition =
appMarketPlaceResourceTest.createAndCheckEntity(req, ADMIN_AUTH_HEADERS); appMarketPlaceResourceTest.createAndCheckEntity(req, ADMIN_AUTH_HEADERS);
} }
@ -54,6 +61,16 @@ public class AppsResourceTest extends EntityResourceTest<App, CreateApp> {
// Does not apply since the App is already validated in the AppMarketDefinition // Does not apply since the App is already validated in the AppMarketDefinition
} }
@Test
void delete_systemApp_400() throws IOException {
CreateApp systemAppRequest = createRequest(SYSTEM_APP_NAME);
App systemApp = createAndCheckEntity(systemAppRequest, ADMIN_AUTH_HEADERS);
assertResponseContains(
() -> deleteEntity(systemApp.getId(), ADMIN_AUTH_HEADERS),
BAD_REQUEST,
"of type SystemApp can not be deleted");
}
@Override @Override
public void validateCreatedEntity( public void validateCreatedEntity(
App createdEntity, CreateApp request, Map<String, String> authHeaders) App createdEntity, CreateApp request, Map<String, String> authHeaders)

View File

@ -195,6 +195,11 @@
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"systemApp": {
"description": "A system app cannot be uninstall.",
"type": "boolean",
"default": false
},
"appConfiguration": { "appConfiguration": {
"description": "Application Configuration object.", "description": "Application Configuration object.",
"$ref": "./configuration/applicationConfig.json#/definitions/appConfig" "$ref": "./configuration/applicationConfig.json#/definitions/appConfig"

View File

@ -130,6 +130,11 @@
"type": "string" "type": "string"
}, },
"uniqueItems": true "uniqueItems": true
},
"systemApp": {
"description": "If the app is a system app, it cannot be uninstalled.",
"type": "boolean",
"default": false
} }
}, },
"additionalProperties": false, "additionalProperties": false,

View File

@ -97,6 +97,11 @@
"type": "string" "type": "string"
}, },
"uniqueItems": true "uniqueItems": true
},
"systemApp": {
"description": "If the app is a system app, it cannot be uninstalled.",
"type": "boolean",
"default": false
} }
}, },
"additionalProperties": false, "additionalProperties": false,