diff --git a/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/errorhandling/DatabaseItemNotFoundException.java b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/errorhandling/DatabaseItemNotFoundException.java new file mode 100644 index 00000000..a22e21cd --- /dev/null +++ b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/errorhandling/DatabaseItemNotFoundException.java @@ -0,0 +1,41 @@ +package org.opendcs.odcsapi.errorhandling; + +import javax.servlet.http.HttpServletResponse; + +public class DatabaseItemNotFoundException extends WebAppException +{ + /** detailed error msg */ + private String errMessage = ""; + + public DatabaseItemNotFoundException(String errMessage) + { + super(HttpServletResponse.SC_NOT_FOUND, errMessage); + this.errMessage = errMessage; + } + + public DatabaseItemNotFoundException(String errMessage, Throwable throwable) + { + super(HttpServletResponse.SC_NOT_FOUND, errMessage, throwable); + this.errMessage = errMessage; + } + + public DatabaseItemNotFoundException() { } + + @Override + public int getStatus() + { + return HttpServletResponse.SC_NOT_FOUND; + } + + @Override + public String getErrMessage() + { + return errMessage; + } + + @Override + public void setErrMessage(String errMessage) + { + this.errMessage = errMessage; + } +} \ No newline at end of file diff --git a/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/errorhandling/MissingParameterException.java b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/errorhandling/MissingParameterException.java new file mode 100644 index 00000000..1bc208a9 --- /dev/null +++ b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/errorhandling/MissingParameterException.java @@ -0,0 +1,41 @@ +package org.opendcs.odcsapi.errorhandling; + +import javax.servlet.http.HttpServletResponse; + +public class MissingParameterException extends WebAppException +{ + /** detailed error msg */ + private String errMessage = ""; + + public MissingParameterException(String errMessage) + { + super(HttpServletResponse.SC_BAD_REQUEST, errMessage); + this.errMessage = errMessage; + } + + public MissingParameterException(String errMessage, Throwable throwable) + { + super(HttpServletResponse.SC_BAD_REQUEST, errMessage, throwable); + this.errMessage = errMessage; + } + + public MissingParameterException() { } + + @Override + public int getStatus() + { + return HttpServletResponse.SC_BAD_REQUEST; + } + + @Override + public String getErrMessage() + { + return errMessage; + } + + @Override + public void setErrMessage(String errMessage) + { + this.errMessage = errMessage; + } +} \ No newline at end of file diff --git a/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/res/AppResources.java b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/res/AppResources.java index 7712669b..054ecdb3 100644 --- a/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/res/AppResources.java +++ b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/res/AppResources.java @@ -52,7 +52,8 @@ import org.opendcs.odcsapi.beans.ApiAppStatus; import org.opendcs.odcsapi.beans.ApiLoadingApp; import org.opendcs.odcsapi.dao.DbException; -import org.opendcs.odcsapi.errorhandling.ErrorCodes; +import org.opendcs.odcsapi.errorhandling.DatabaseItemNotFoundException; +import org.opendcs.odcsapi.errorhandling.MissingParameterException; import org.opendcs.odcsapi.errorhandling.WebAppException; import org.opendcs.odcsapi.lrgsclient.ClientConnectionCache; import org.opendcs.odcsapi.sec.AuthorizationCheck; @@ -111,8 +112,7 @@ public Response getApp(@QueryParam("appid") Long appId) { if (appId == null) { - throw new WebAppException(HttpServletResponse.SC_BAD_REQUEST, - "Missing required appid parameter."); + throw new MissingParameterException("Missing required appid parameter."); } try (LoadingAppDAI dai = getLegacyDatabase().makeLoadingAppDAO()) { @@ -142,7 +142,7 @@ public Response postApp(ApiLoadingApp app) { CompAppInfo compApp = map(app); dai.writeComputationApp(compApp); - return Response.status(HttpServletResponse.SC_OK) + return Response.status(HttpServletResponse.SC_CREATED) .entity(map(compApp)) .build(); } @@ -186,7 +186,7 @@ public Response deleteApp(@QueryParam("appid") Long appId) { if (appId == null) { - throw new WebAppException(HttpServletResponse.SC_BAD_REQUEST, "Missing required appid parameter."); + throw new MissingParameterException("Missing required appid parameter."); } try (LoadingAppDAI dai = getLegacyDatabase().makeLoadingAppDAO()) @@ -197,13 +197,12 @@ public Response deleteApp(@QueryParam("appid") Long appId) throw new DbException(String.format(NO_APP_FOUND, appId)); } dai.deleteComputationApp(app); - return Response.status(HttpServletResponse.SC_OK) + return Response.status(HttpServletResponse.SC_NO_CONTENT) .entity("appId with ID " + appId + " deleted").build(); } catch (NoSuchObjectException e) { - return Response.status(HttpServletResponse.SC_NOT_FOUND) - .entity(String.format(NO_APP_FOUND, appId)).build(); + throw new DatabaseItemNotFoundException(String.format(NO_APP_FOUND, appId), e); } catch (DbIoException | ConstraintException ex) { @@ -269,6 +268,11 @@ static ApiAppStatus map(LoadingAppDAI dai, TsdbCompLock lock) throws DbIoExcepti public Response getAppEvents(@QueryParam("appid") Long appId) throws WebAppException, DbException { + if (appId == null) + { + throw new MissingParameterException("Missing required appid parameter."); + } + HttpSession session = request.getSession(true); ClientConnectionCache clientConnectionCache = ClientConnectionCache.getInstance(); Optional cli = clientConnectionCache.getApiEventClient(appId, session.getId()); @@ -280,19 +284,19 @@ public Response getAppEvents(@QueryParam("appid") Long appId) if (appStat == null) { cli.ifPresent(c -> clientConnectionCache.removeApiEventClient(c, session.getId())); - throw new WebAppException(HttpServletResponse.SC_NOT_FOUND, "appid " + appId + throw new DatabaseItemNotFoundException("appid " + appId + " is not running (no lock found)."); } else if (appStat.getPid() == null) { cli.ifPresent(c -> clientConnectionCache.removeApiEventClient(c, session.getId())); - throw new WebAppException(HttpServletResponse.SC_NOT_FOUND, "appid " + appId + throw new DatabaseItemNotFoundException("appid " + appId + " (" + appStat.getAppName() + ") is not running."); } else if (System.currentTimeMillis() - appStat.getHeartbeat().getTime() > 20000L) { cli.ifPresent(c -> clientConnectionCache.removeApiEventClient(c, session.getId())); - throw new WebAppException(HttpServletResponse.SC_NOT_FOUND, "appid " + appId + throw new DatabaseItemNotFoundException("appid " + appId + " (" + appStat.getAppName() + ") is not running (stale heartbeat)."); } else if (appStat.getPid() != null && (!cli.isPresent() || appStat.getPid() != cli.get().getPid())) @@ -314,21 +318,21 @@ else if (appStat.getPid() != null && (!cli.isPresent() || appStat.getPid() != cl } if(apiEventClient == null) { - throw new WebAppException(HttpServletResponse.SC_NOT_FOUND, "No API Event Client found or created"); + throw new DatabaseItemNotFoundException("No API Event Client found or created"); } return Response.status(HttpServletResponse.SC_OK) .entity(apiEventClient.getNewEvents()).build(); } catch(ConnectException ex) { - throw new WebAppException(ErrorCodes.IO_ERROR, + throw new WebAppException(HttpServletResponse.SC_CONFLICT, String.format("Cannot connect to %s.", appStat.getAppName()), ex); // Connection failed, event client not added to user token } catch(IOException ex) { cli.ifPresent(c -> clientConnectionCache.removeApiEventClient(c, session.getId())); - throw new WebAppException(ErrorCodes.IO_ERROR, + throw new WebAppException(HttpServletResponse.SC_CONFLICT, String.format("Event socket to %s closed by app", appStat.getAppId()), ex); } } @@ -343,8 +347,7 @@ public Response postAppStart(@QueryParam("appid") Long appId) { if (appId == null) { - throw new WebAppException(HttpServletResponse.SC_BAD_REQUEST, - "appId parameter required for this operation."); + throw new MissingParameterException("appId parameter required for this operation."); } try (LoadingAppDAI dai = getLegacyDatabase().makeLoadingAppDAO()) @@ -356,13 +359,13 @@ public Response postAppStart(@QueryParam("appid") Long appId) // Error if already running and heartbeat is current if (appStat != null && appStat.getPid() != null && appStat.getHeartbeat() != null && (System.currentTimeMillis() - appStat.getHeartbeat().getTime() < 20000L)) - throw new WebAppException(ErrorCodes.NOT_ALLOWED, + throw new WebAppException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "App id=" + appId + " (" + loadingApp.getAppName() + ") is already running."); // Error if no "startCmd" property String startCmd = ApiPropertiesUtil.getIgnoreCase(loadingApp.getProperties(), "startCmd"); if (startCmd == null) - throw new WebAppException(ErrorCodes.BAD_CONFIG, + throw new WebAppException(HttpServletResponse.SC_PRECONDITION_FAILED, "App id=" + appId + " (" + loadingApp.getAppName() + ") has no 'startCmd' property."); @@ -391,7 +394,7 @@ public Response postAppStart(@QueryParam("appid") Long appId) } catch (DbIoException | NoSuchObjectException | IOException | LockBusyException ex) { - throw new WebAppException(ErrorCodes.DATABASE_ERROR, + throw new WebAppException(HttpServletResponse.SC_BAD_REQUEST, String.format("Error attempting to start appId=%s", appId), ex); } } @@ -419,8 +422,7 @@ public Response postAppStop(@QueryParam("appid") Long appId) { if (appId == null) { - throw new WebAppException(HttpServletResponse.SC_BAD_REQUEST, - "appId parameter required for this operation."); + throw new MissingParameterException("appId parameter required for this operation."); } try (LoadingAppDAI dai = getLegacyDatabase().makeLoadingAppDAO()) @@ -432,7 +434,7 @@ public Response postAppStop(@QueryParam("appid") Long appId) if (appStat == null || appStat.getPid() == null) { - throw new WebAppException(HttpServletResponse.SC_NOT_FOUND, + throw new DatabaseItemNotFoundException( "appId " + appId + "(" + loadingApp.getAppName() + ") not currently running."); } @@ -444,12 +446,11 @@ public Response postAppStop(@QueryParam("appid") Long appId) } catch (NoSuchObjectException e) { - return Response.status(HttpServletResponse.SC_NOT_FOUND) - .entity("No such app found with ID " + appId).build(); + throw new DatabaseItemNotFoundException(String.format("No such app found with ID %s", appId), e); } catch (DbIoException ex) { - throw new WebAppException(ErrorCodes.DATABASE_ERROR, + throw new WebAppException(HttpServletResponse.SC_BAD_REQUEST, String.format("Error attempting to stop appId=%s", appId), ex); } } diff --git a/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/it/AppResourcesIT.java b/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/it/AppResourcesIT.java index f09bf84c..9e9dd95b 100644 --- a/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/it/AppResourcesIT.java +++ b/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/it/AppResourcesIT.java @@ -25,7 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -@Tag("integration") +@Tag("integration-opentsdb-only") @ExtendWith(DatabaseContextProvider.class) final class AppResourcesIT extends BaseIT { @@ -55,7 +55,7 @@ void setUp() throws Exception .then() .log().ifValidationFails(LogDetail.ALL, true) .assertThat() - .statusCode(is(HttpServletResponse.SC_OK)) + .statusCode(is(HttpServletResponse.SC_CREATED)) .extract() ; @@ -78,7 +78,7 @@ void tearDown() .then() .log().ifValidationFails(LogDetail.ALL, true) .assertThat() - .statusCode(is(HttpServletResponse.SC_OK)) + .statusCode(is(HttpServletResponse.SC_NO_CONTENT)) ; logout(sessionFilter); @@ -176,7 +176,7 @@ void testPostAndDeleteApp() throws Exception .then() .log().ifValidationFails(LogDetail.ALL, true) .assertThat() - .statusCode(is(HttpServletResponse.SC_OK)) + .statusCode(is(HttpServletResponse.SC_CREATED)) .extract(); // Get app ID from response @@ -222,7 +222,7 @@ void testPostAndDeleteApp() throws Exception .then() .log().ifValidationFails(LogDetail.ALL, true) .assertThat() - .statusCode(is(HttpServletResponse.SC_OK)) + .statusCode(is(HttpServletResponse.SC_NO_CONTENT)) ; // Assert that the app was deleted