diff --git a/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/beans/DbCompFilter.java b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/beans/DbCompFilter.java new file mode 100644 index 000000000..6d828e039 --- /dev/null +++ b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/beans/DbCompFilter.java @@ -0,0 +1,284 @@ +/* + * Copyright 2025 OpenDCS Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.opendcs.odcsapi.beans; + + +import decodes.tsdb.DbComputation; +import java.util.function.Predicate; + +/** + * A filter used by the Comp Ref retrieval method of the REST API. This filter is used + * to filter the Db Computations that show up in the response. + */ +public class DbCompFilter implements Predicate +{ + protected String site; + protected String dataType; + protected String intervalCode = null; + protected String process; + protected String algorithm; + protected boolean enabledOnly = false; + protected String group; + + // internal string validation + private final Predicate validString = s -> (s != null) && !s.isEmpty(); + private final Predicate validSite = comp -> { + if (site != null && !site.isEmpty()) + { + if (comp.getParmList() == null || comp.getParmList().isEmpty()) + { + return false; + } + else + { + return comp.getParmList() + .stream() + .anyMatch(item -> { + if (item.getSiteName() == null + || item.getSiteName().getNameValue() == null + || item.getSiteName().getNameValue().isEmpty()) + { + return false; + } + else + { + return item.getSiteName().getNameValue().equalsIgnoreCase(site); + } + }); + } + } + else + { + return true; + } + }; + + private final Predicate validDataType = comp -> + { + if (dataType != null + && !dataType.isEmpty()) + { + if (comp.getParmList() == null || comp.getParmList().isEmpty()) + { + return false; + } + return comp.getParmList() + .stream() + .anyMatch(item -> { + if (item.getDataType() == null + || item.getDataType().getDisplayName() == null + || item.getDataType().getDisplayName().isEmpty()) + { + return false; + } + else + { + return item.getDataType().getDisplayName().equalsIgnoreCase(dataType); + } + }); + } + else + { + return true; + } + }; + + private final Predicate validInterval = comp -> { + if (intervalCode != null + && !intervalCode.isEmpty()) + { + if (comp.getParmList() == null || comp.getParmList().isEmpty()) + { + return false; + } + else + { + return comp.getParmList() + .stream() + .anyMatch(item -> + { + if (item.getInterval() == null) + { + return false; + } + else + { + return item.getInterval().equalsIgnoreCase(intervalCode); + } + }); + } + } + else + { + return true; + } + }; + + // external value validation + private final Predicate validProcess = match -> + { + if (process == null || process.isEmpty()) + { + return true; + } + else + { + return process.equalsIgnoreCase(match); + } + }; + + private final Predicate validAlgorithm = match -> { + if (algorithm == null || algorithm.isEmpty()) + { + return true; + } + else + { + return algorithm.equalsIgnoreCase(match); + } + }; + + private final Predicate validGroup = match -> { + if (group == null || group.isEmpty()) + { + return true; + } + else + { + return group.equalsIgnoreCase(match); + } + }; + + private final Predicate enabled = match -> { + if (enabledOnly) + { + return match; + } + else + { + return true; + } + }; + + public boolean test(DbComputation comp) + { + return validProcess.test(comp.getApplicationName()) + && validAlgorithm.test(comp.getAlgorithmName()) + && enabled.test(comp.isEnabled()) + && validGroup.test(comp.getGroupName()) + && validSite.test(comp) + && validDataType.test(comp) + && validInterval.test(comp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder("CompFilter: "); + if (validString.test(site)) + sb.append("siteId=" + site + " "); + if (validString.test(dataType)) + sb.append("dataTypeId=" + dataType + " "); + if (validString.test(intervalCode)) + sb.append("intervalCode=" + intervalCode + " "); + if (validString.test(process)) + sb.append("processId=" + process + " "); + if (validString.test(algorithm)) + sb.append("algoId=" + algorithm + " "); + if (validString.test(group)) + sb.append("groupId=" + group + " "); + + return sb.toString(); + } + + /** Constructor */ + public DbCompFilter() + { + // Empty constructor + } + + public void setSite(String x) + { + site = x; + } + + public void setDataType(String x) + { + dataType = x; + } + + public void setIntervalCode(String x) + { + if (x != null && x.trim().isEmpty()) + x = null; + intervalCode = x; + } + + public void setProcess(String x) + { + process = x; + } + + public void setAlgorithm(String x) + { + algorithm = x; + } + + public String getSite() + { + return site; + } + + public String getDataType() + { + return dataType; + } + + public String getIntervalCode() + { + return intervalCode; + } + + public String getProcess() + { + return process; + } + + public String getAlgorithm() + { + return algorithm; + } + + public void setEnabledOnly(boolean enabledOnly) + { + this.enabledOnly = enabledOnly; + } + + public void setGroup(String group) + { + this.group = group; + } + + public String getGroup() + { + return group; + } + + public boolean isEnabledOnly() + { + return enabledOnly; + } +} + diff --git a/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/res/ComputationResources.java b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/res/ComputationResources.java index bd62fe2b9..38a1c45f6 100644 --- a/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/res/ComputationResources.java +++ b/opendcs-rest-api/src/main/java/org/opendcs/odcsapi/res/ComputationResources.java @@ -15,9 +15,11 @@ package org.opendcs.odcsapi.res; -import java.sql.SQLException; -import java.util.logging.Logger; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import javax.annotation.security.RolesAllowed; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -30,79 +32,359 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import decodes.db.DataType; +import decodes.db.Site; +import decodes.sql.DbKey; +import decodes.tsdb.ConstraintException; +import decodes.tsdb.DbCompAlgorithm; +import decodes.tsdb.DbCompParm; +import decodes.tsdb.DbComputation; +import decodes.tsdb.DbIoException; +import decodes.tsdb.NoSuchObjectException; +import decodes.tsdb.TsGroup; +import decodes.tsdb.compedit.ComputationInList; +import opendcs.dai.ComputationDAI; +import org.opendcs.odcsapi.beans.ApiCompParm; import org.opendcs.odcsapi.beans.ApiComputation; -import org.opendcs.odcsapi.dao.ApiComputationDAO; +import org.opendcs.odcsapi.beans.ApiComputationRef; +import org.opendcs.odcsapi.beans.DbCompFilter; 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.hydrojson.DbInterface; import org.opendcs.odcsapi.util.ApiConstants; -import org.opendcs.odcsapi.util.ApiHttpUtil; @Path("/") -public class ComputationResources +public class ComputationResources extends OpenDcsResource { @Context HttpHeaders httpHeaders; - + @GET @Path("computationrefs") @Produces(MediaType.APPLICATION_JSON) @RolesAllowed({ApiConstants.ODCS_API_GUEST}) public Response getComputationRefs(@QueryParam("site") String site, - @QueryParam("algorithm") String algorithm, - @QueryParam("datatype") String datatype, - @QueryParam("group") String group, - @QueryParam("process") String process, - @QueryParam("enabled") Boolean enabled, - @QueryParam("interval") String interval) - throws WebAppException, DbException + @QueryParam("algorithm") String algorithm, + @QueryParam("datatype") String datatype, + @QueryParam("group") String group, + @QueryParam("process") String process, + @QueryParam("enabled") Boolean enabled, + @QueryParam("interval") String interval) + throws DbException, WebAppException { - Logger.getLogger(ApiConstants.loggerName).fine("getComputationRefs"); - try (DbInterface dbi = new DbInterface(); - ApiComputationDAO dao = new ApiComputationDAO(dbi)) + try (ComputationDAI dai = getLegacyTimeseriesDB().makeComputationDAO()) + { + DbCompFilter refFilter = new DbCompFilter(); + if (enabled != null) + { + refFilter.setEnabledOnly(enabled); + } + if (algorithm != null) + { + refFilter.setAlgorithm(algorithm); + } + if (datatype != null) + { + refFilter.setDataType(datatype); + } + if (group != null) + { + refFilter.setGroup(group); + } + if (process != null) + { + refFilter.setProcess(process); + } + if (site != null) + { + refFilter.setSite(site); + } + if (interval != null) + { + refFilter.setIntervalCode(interval); + } + List comps = dai.listComps(refFilter) + .stream() + .map(ComputationResources::map) + .collect(Collectors.toList()); + if (comps.isEmpty()) + { + throw new DatabaseItemNotFoundException("No computations found matching the filter criteria"); + } + return Response.status(HttpServletResponse.SC_OK).entity(comps).build(); + } + catch(DbIoException e) { - return ApiHttpUtil.createResponse(dao.getComputationRefs(site, algorithm, datatype, group, - process, enabled, interval)); + throw new DbException("Unable to retrieve computation references", e); } } + static ArrayList map(ArrayList computations) + { + ArrayList ret = new ArrayList<>(); + for (ComputationInList comp : computations) + { + ApiComputationRef ref = new ApiComputationRef(); + ref.setComputationId(comp.getComputationId().getValue()); + if (comp.getAlgorithmId() != null) + { + ref.setAlgorithmId(comp.getAlgorithmId().getValue()); + } + else + { + ref.setAlgorithmId(DbKey.NullKey.getValue()); + } + ref.setAlgorithmName(comp.getAlgorithmName()); + ref.setName(comp.getComputationName()); + ref.setEnabled(comp.isEnabled()); + ref.setDescription(comp.getDescription()); + ref.setProcessName(comp.getProcessName()); + if (comp.getProcessId() != null) + { + ref.setProcessId(comp.getProcessId().getValue()); + } + else + { + ref.setProcessId(DbKey.NullKey.getValue()); + } + ret.add(ref); + } + return ret; + } + @GET @Path("computation") @Produces(MediaType.APPLICATION_JSON) @RolesAllowed({ApiConstants.ODCS_API_GUEST}) public Response getComputation(@QueryParam("computationid") Long compId) - throws WebAppException, DbException + throws WebAppException, DbException { if (compId == null) - throw new WebAppException(ErrorCodes.MISSING_ID, - "Missing required computationid parameter."); - - Logger.getLogger(ApiConstants.loggerName).fine("getComputation computationid=" + compId); + { + throw new MissingParameterException("Missing required computationid parameter."); + } + + try (ComputationDAI dai = getLegacyTimeseriesDB().makeComputationDAO()) + { + return Response.status(HttpServletResponse.SC_OK) + .entity(map(dai.getComputationById(DbKey.createDbKey(compId)))).build(); + } + catch(DbIoException e) + { + throw new DbException(String.format("Unable to retrieve computation by ID: %s", compId), e); + } + catch (NoSuchObjectException e) + { + throw new DatabaseItemNotFoundException(String.format("Computation with ID %s not found", compId)); + } + } + + static ApiComputation map(DbComputation comp) + { + ApiComputation ret = new ApiComputation(); + if (comp.getId() != null) + { + ret.setComputationId(comp.getId().getValue()); + } + else + { + ret.setComputationId(DbKey.NullKey.getValue()); + } + if (comp.getAlgorithmId() != null) + { + ret.setAlgorithmId(comp.getAlgorithmId().getValue()); + } + else + { + ret.setAlgorithmId(DbKey.NullKey.getValue()); + } + ret.setComment(comp.getComment()); + if (comp.getAppId() != null) + { + ret.setAppId(comp.getAppId().getValue()); + } + else + { + ret.setAppId(DbKey.NullKey.getValue()); + } + ret.setEnabled(comp.isEnabled()); + ret.setEffectiveEndDate(comp.getValidEnd()); + ret.setEffectiveStartDate(comp.getValidStart()); + ret.setAlgorithmName(comp.getAlgorithmName()); + ret.setApplicationName(comp.getApplicationName()); + ret.setGroupName(comp.getGroupName()); + ret.setName(comp.getName()); + ret.setLastModified(comp.getLastModified()); + if (comp.getGroupId() != null) + { + ret.setGroupId(comp.getGroupId().getValue()); + } + else + { + ret.setGroupId(DbKey.NullKey.getValue()); + } + ret.setProps(comp.getProperties()); + ret.setParmList(new ArrayList<>(comp.getParmList() + .stream() + .map(ComputationResources::map) + .collect(Collectors.toList()))); + return ret; + } - try (DbInterface dbi = new DbInterface(); - ApiComputationDAO dao = new ApiComputationDAO(dbi)) + static ApiCompParm map(DbCompParm parm) + { + ApiCompParm ret = new ApiCompParm(); + if (parm.getDataType() != null) + { + ret.setDataType(parm.getDataType().getDisplayName()); + } + ret.setInterval(parm.getInterval()); + if (parm.getSiteName() != null) + { + ret.setSiteName(parm.getSiteName().getDisplayName()); + } + if (parm.getSiteId() != null) + { + ret.setSiteId(parm.getSiteId().getValue()); + } + else { - return ApiHttpUtil.createResponse(dao.getComputation(compId)); + ret.setSiteId(DbKey.NullKey.getValue()); } + ret.setUnitsAbbr(parm.getUnitsAbbr()); + ret.setAlgoParmType(parm.getAlgoParmType()); + ret.setAlgoRoleName(parm.getRoleName()); + ret.setDuration(parm.getDuration()); + ret.setInterval(parm.getInterval()); + ret.setDeltaT(parm.getDeltaT()); + if (parm.getDataTypeId() != null) + { + ret.setDataTypeId(parm.getDataTypeId().getValue()); + } + else + { + ret.setDataTypeId(DbKey.NullKey.getValue()); + } + ret.setDeltaTUnits(parm.getDeltaTUnits()); + ret.setVersion(parm.getVersion()); + ret.setModelId(parm.getModelId()); + ret.setTableSelector(parm.getTableSelector()); + ret.setParamType(parm.getParamType()); + return ret; } - @POST @Path("computation") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @RolesAllowed({ApiConstants.ODCS_API_ADMIN, ApiConstants.ODCS_API_USER}) public Response postComputation(ApiComputation comp) - throws WebAppException, DbException, SQLException + throws DbException + { + try (ComputationDAI dai = getLegacyTimeseriesDB().makeComputationDAO()) + { + DbComputation dbComp = map(comp); + dai.writeComputation(dbComp); + return Response.status(HttpServletResponse.SC_CREATED).entity(map(dbComp)).build(); + } + catch(DbIoException e) + { + throw new DbException("Unable to store computation", e); + } + } + + static DbComputation map(ApiComputation comp) + { + DbComputation ret; + if (comp.getComputationId() != null) + { + ret = new DbComputation(DbKey.createDbKey(comp.getComputationId()), comp.getName()); + } + else + { + ret = new DbComputation(DbKey.NullKey, comp.getName()); + } + if (comp.getAlgorithmId() != null) + { + ret.setAlgorithmId(DbKey.createDbKey(comp.getAlgorithmId())); + } + if (comp.getAppId() != null) + { + ret.setAppId(DbKey.createDbKey(comp.getAppId())); + } + ret.setComment(comp.getComment()); + ret.setEnabled(comp.isEnabled()); + ret.setValidEnd(comp.getEffectiveEndDate()); + ret.setValidStart(comp.getEffectiveStartDate()); + ret.setAlgorithmName(comp.getAlgorithmName()); + if (comp.getAlgorithmId() != null) + { + ret.setAlgorithm(new DbCompAlgorithm(DbKey.createDbKey(comp.getAlgorithmId()), + comp.getAlgorithmName(), null, comp.getComment())); + } + ret.setApplicationName(comp.getApplicationName()); + ret.setGroup(new TsGroup().copy(comp.getGroupName())); + ret.setLastModified(comp.getLastModified()); + if (comp.getGroupId() != null) + { + ret.setGroupId(DbKey.createDbKey(comp.getGroupId())); + } + else + { + ret.setGroupId(DbKey.NullKey); + } + for (String prop : comp.getProps().stringPropertyNames()) + { + ret.setProperty(prop, comp.getProps().getProperty(prop)); + } + for (ApiCompParm parm : comp.getParmList()) + { + ret.addParm(map(parm)); + } + return ret; + } + + static DbCompParm map(ApiCompParm parm) { - Logger.getLogger(ApiConstants.loggerName).fine("post comp received comp " + comp.getName() - + " with ID=" + comp.getComputationId()); - - try (DbInterface dbi = new DbInterface(); - ApiComputationDAO dao = new ApiComputationDAO(dbi)) + if (parm == null) + { + return null; + } + DbCompParm ret = new DbCompParm(parm.getAlgoRoleName(), + parm.getDataTypeId() != null ? DbKey.createDbKey(parm.getDataTypeId()) : DbKey.NullKey, + parm.getInterval(), parm.getTableSelector(), parm.getDeltaT()); + if (parm.getDataTypeId() != null) + { + DataType dt = new DataType(parm.getDataType(), parm.getDataTypeId().toString()); + ret.setDataType(dt); + } + ret.setInterval(parm.getInterval()); + if (parm.getSiteId() != null) { - return ApiHttpUtil.createResponse(dao.writeComputation(comp)); + Site site = new Site(); + site.setPublicName(parm.getSiteName()); + ret.setSite(site); + ret.setSiteId(DbKey.createDbKey(parm.getSiteId())); } + else + { + ret.setSiteId(DbKey.NullKey); + } + ret.setUnitsAbbr(parm.getUnitsAbbr()); + ret.setAlgoParmType(parm.getAlgoParmType()); + ret.setRoleName(parm.getAlgoRoleName()); + ret.setInterval(parm.getInterval()); + ret.setDeltaT(parm.getDeltaT()); + ret.setDeltaTUnits(parm.getDeltaTUnits()); + if (parm.getModelId() != null) + { + ret.setModelId(parm.getModelId()); + } + ret.setTableSelector(parm.getTableSelector()); + ret.setInterval(parm.getInterval()); + ret.setDeltaT(parm.getDeltaT()); + ret.setUnitsAbbr(parm.getUnitsAbbr()); + return ret; } @DELETE @@ -110,18 +392,23 @@ public Response postComputation(ApiComputation comp) @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @RolesAllowed({ApiConstants.ODCS_API_ADMIN, ApiConstants.ODCS_API_USER}) - public Response deleteComputation(@QueryParam("computationid") Long computationId) throws DbException + public Response deleteComputation(@QueryParam("computationid") Long computationId) + throws DbException, WebAppException { - Logger.getLogger(ApiConstants.loggerName).fine( - "DELETE computation received computationId=" + computationId); - - // Use username and password to attempt to connect to the database - try (DbInterface dbi = new DbInterface(); - ApiComputationDAO dao = new ApiComputationDAO(dbi)) + if (computationId == null) { - dao.deleteComputation(computationId); - return ApiHttpUtil.createResponse("Computation with ID " + computationId + " deleted"); + throw new MissingParameterException("Missing required computationid parameter."); + } + try (ComputationDAI dai = getLegacyTimeseriesDB().makeComputationDAO()) + { + dai.deleteComputation(DbKey.createDbKey(computationId)); + return Response.status(HttpServletResponse.SC_NO_CONTENT) + .entity("Computation with ID " + computationId + " deleted").build(); + } + catch(DbIoException | ConstraintException e) + { + throw new DbException(String.format("Unable to delete computation by ID: %s", computationId), e); } } } diff --git a/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/ComputationResourcesTest.java b/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/ComputationResourcesTest.java new file mode 100644 index 000000000..e6b9fb563 --- /dev/null +++ b/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/ComputationResourcesTest.java @@ -0,0 +1,138 @@ +package org.opendcs.odcsapi.res; + +import java.sql.Date; +import java.time.Instant; +import java.util.ArrayList; +import java.util.stream.Collectors; + +import decodes.sql.DbKey; +import decodes.tsdb.DbCompAlgorithm; +import decodes.tsdb.DbComputation; +import decodes.tsdb.TsGroup; +import decodes.tsdb.compedit.ComputationInList; +import org.junit.jupiter.api.Test; +import org.opendcs.odcsapi.beans.ApiComputation; +import org.opendcs.odcsapi.beans.ApiComputationRef; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opendcs.odcsapi.res.ComputationResources.map; + +final class ComputationResourcesTest +{ + @Test + void testApiComputeMap() + { + DbKey dbKey = DbKey.createDbKey(16704L); + String name = "Computation"; + DbComputation dbComp = new DbComputation(dbKey, name); + dbComp.setAlgorithmName("Computation Algorithm"); + dbComp.setComment("Computation to find the average of a set of values"); + dbComp.setAppId(DbKey.createDbKey(18596L)); + dbComp.setLastModified(Date.from(Instant.parse("2023-08-01T10:30:00Z"))); + dbComp.setAlgorithm(new DbCompAlgorithm(dbComp.getAlgorithmName())); + dbComp.setEnabled(true); + dbComp.setApplicationName("Average Application"); + dbComp.setValidStart(Date.from(Instant.parse("2023-07-01T00:30:00Z"))); + TsGroup group = new TsGroup(); + group.setGroupName("Computation group"); + group.setGroupId(DbKey.createDbKey(16753096L)); + dbComp.setGroup(group); + dbComp.setEnabled(true); + dbComp.setAlgorithmId(DbKey.createDbKey(197865L)); + dbComp.setValidEnd(Date.from(Instant.parse("2023-08-03T00:30:00Z"))); + + ApiComputation apiComp = map(dbComp); + + assertNotNull(apiComp); + assertEquals(dbComp.getId().getValue(), apiComp.getComputationId()); + assertEquals(dbComp.getAlgorithmName(), apiComp.getAlgorithmName()); + assertEquals(dbComp.getComment(), apiComp.getComment()); + assertEquals(dbComp.getAppId().getValue(), apiComp.getAppId()); + assertEquals(dbComp.getLastModified(), apiComp.getLastModified()); + assertEquals(dbComp.getAlgorithm().getName(), apiComp.getAlgorithmName()); + assertEquals(dbComp.getAlgorithmId().getValue(), apiComp.getAlgorithmId()); + assertEquals(dbComp.isEnabled(), apiComp.isEnabled()); + assertEquals(dbComp.getApplicationName(), apiComp.getApplicationName()); + assertEquals(dbComp.getValidStart(), apiComp.getEffectiveStartDate()); + assertEquals(dbComp.getValidEnd(), apiComp.getEffectiveEndDate()); + assertTrue(apiComp.isEnabled()); + assertEquals(dbComp.getGroup().getGroupName(), apiComp.getGroupName()); + assertEquals(dbComp.getGroupId().getValue(), apiComp.getGroupId()); + assertEquals(dbComp.getName(), apiComp.getName()); + assertEquals(dbComp.getProperties(), apiComp.getProps()); + assertEquals(dbComp.getParmList().stream().map(ComputationResources::map).collect(Collectors.toList()), + apiComp.getParmList()); + } + + @Test + void testDbComputeMap() + { + ApiComputation apiComp = new ApiComputation(); + apiComp.setComputationId(16704L); + apiComp.setName("Area Computation"); + apiComp.setAlgorithmName("Area Algorithm"); + apiComp.setComment("Computation to find the area of a given object"); + apiComp.setAppId(1L); + apiComp.setLastModified(Date.from(Instant.parse("2023-08-01T10:30:00Z"))); + apiComp.setEnabled(true); + apiComp.setApplicationName("Area Application"); + apiComp.setEffectiveStartDate(Date.from(Instant.parse("2023-07-01T00:30:00Z"))); + apiComp.setEffectiveEndDate(Date.from(Instant.parse("2023-08-03T00:30:00Z"))); + apiComp.setAlgorithmId(197865L); + apiComp.setGroupName("Computation Group"); + apiComp.setGroupId(16753096L); + DbComputation dbComp = map(apiComp); + assertNotNull(dbComp); + assertEquals(apiComp.getComputationId(), dbComp.getKey().getValue()); + assertEquals(apiComp.getName(), dbComp.getName()); + assertEquals(apiComp.getAlgorithmName(), dbComp.getAlgorithmName()); + assertEquals(apiComp.getComment(), dbComp.getComment()); + assertEquals(apiComp.getAppId(), dbComp.getAppId().getValue()); + assertEquals(apiComp.getLastModified(), dbComp.getLastModified()); + assertEquals(apiComp.getAlgorithmName(), dbComp.getAlgorithm().getName()); + assertEquals(apiComp.isEnabled(), dbComp.isEnabled()); + assertEquals(apiComp.getApplicationName(), dbComp.getApplicationName()); + assertEquals(apiComp.getEffectiveStartDate(), dbComp.getValidStart()); + assertEquals(apiComp.getEffectiveEndDate(), dbComp.getValidEnd()); + assertTrue(dbComp.isEnabled()); + assertEquals(apiComp.getGroupName(), dbComp.getGroup().getGroupName()); + assertEquals(apiComp.getGroupId(), dbComp.getGroupId().getValue()); + assertEquals(apiComp.getName(), dbComp.getName()); + assertEquals(apiComp.getProps(), dbComp.getProperties()); + assertEquals(apiComp.getParmList().stream().map(ComputationResources::map).collect(Collectors.toList()), + dbComp.getParmList()); + assertEquals(apiComp.getAlgorithmId(), dbComp.getAlgorithm().getId().getValue()); + } + + @Test + void testComputationRefMap() + { + ArrayList comps = new ArrayList<>(); + ComputationInList dbComp = new ComputationInList(DbKey.createDbKey(16704L), "Flow Computation", + DbKey.createDbKey(197865L), DbKey.createDbKey(51981L), + true, "Computation to find the flow rate of a body of water"); + dbComp.setAlgorithmName("Flow Algorithm"); + dbComp.setDescription("Computation to determine flow rate"); + dbComp.setProcessId(DbKey.createDbKey(1L)); + DbCompAlgorithm dbCompAlgorithm = new DbCompAlgorithm("Flow Algorithm"); + dbCompAlgorithm.setId(DbKey.createDbKey(197865L)); + dbComp.setEnabled(true); + comps.add(dbComp); + + ArrayList apiComps = map(comps); + + assertNotNull(apiComps); + assertEquals(1, apiComps.size()); + ApiComputationRef apiComp = apiComps.get(0); + assertNotNull(apiComp); + assertEquals(dbComp.getComputationId().getValue(), apiComp.getComputationId()); + assertEquals(dbComp.getComputationName(), apiComp.getName()); + assertEquals(dbComp.getAlgorithmName(), apiComp.getAlgorithmName()); + assertEquals(dbComp.getDescription(), apiComp.getDescription()); + assertEquals(dbComp.getProcessId().getValue(), apiComp.getProcessId()); + assertEquals(dbComp.getProcessName(), apiComp.getProcessName()); + assertEquals(dbComp.getAlgorithmId().getValue(), apiComp.getAlgorithmId()); + } +} diff --git a/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/it/ComputationResourcesIT.java b/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/it/ComputationResourcesIT.java new file mode 100644 index 000000000..3f2162bed --- /dev/null +++ b/opendcs-rest-api/src/test/java/org/opendcs/odcsapi/res/it/ComputationResourcesIT.java @@ -0,0 +1,527 @@ +package org.opendcs.odcsapi.res.it; + +import java.util.List; +import java.util.Map; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.MediaType; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.restassured.filter.log.LogDetail; +import io.restassured.filter.session.SessionFilter; +import io.restassured.path.json.JsonPath; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.opendcs.odcsapi.beans.ApiAlgorithm; +import org.opendcs.odcsapi.beans.ApiCompParm; +import org.opendcs.odcsapi.beans.ApiComputation; +import org.opendcs.odcsapi.beans.ApiLoadingApp; +import org.opendcs.odcsapi.beans.ApiSite; +import org.opendcs.odcsapi.fixtures.DatabaseContextProvider; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +@Tag("integration-opentsdb-only") +@ExtendWith(DatabaseContextProvider.class) +final class ComputationResourcesIT extends BaseIT +{ + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static SessionFilter sessionFilter; + private Long compId; + private Long siteId; + private Long appId; + private Long algId; + + @BeforeEach + public void init() throws Exception + { + setUpCreds(); + sessionFilter = new SessionFilter(); + authenticate(sessionFilter); + + ApiComputation comp = getDtoFromResource("computation_insert_data.json", ApiComputation.class); + + ApiSite site = getDtoFromResource("computation_site_data.json", ApiSite.class); + + ApiLoadingApp app = getDtoFromResource("computation_app_data.json", ApiLoadingApp.class); + String appJson = MAPPER.writeValueAsString(app); + + // Insert the app + ExtractableResponse response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .body(appJson) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("app") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .extract() + ; + + appId = response.body().jsonPath().getLong("appId"); + + // Insert Algorithm + ApiAlgorithm alg = getDtoFromResource("computation_algorithm_data.json", ApiAlgorithm.class); + + String algJson = MAPPER.writeValueAsString(alg); + + // Insert the algorithm + response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .body(algJson) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("algorithm") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)) + .extract() + ; + + algId = response.body().jsonPath().getLong("algorithmId"); + + String siteJson = MAPPER.writeValueAsString(site); + + // Insert the site + response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .body(siteJson) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("site") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .extract() + ; + + siteId = response.body().jsonPath().getLong("siteId"); + + comp.getParmList().get(0).setSiteName(site.getPublicName()); + comp.getParmList().get(0).setSiteId(siteId); + comp.setApplicationName(app.getAppName()); + comp.setAppId(appId); + comp.setAlgorithmName(alg.getName()); + comp.setAlgorithmId(algId); + + String compJson = MAPPER.writeValueAsString(comp); + + // Insert the computation + response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .body(compJson) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("computation") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)) + .extract() + ; + + compId = response.body().jsonPath().getLong("computationId"); + } + + @AfterEach + public void cleanup() + { + // Delete the computation + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("computationid", compId) + .when() + .redirects().follow(true) + .redirects().max(3) + .delete("computation") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NO_CONTENT)) + ; + + // Delete the app + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("appid", appId) + .when() + .redirects().follow(true) + .redirects().max(3) + .delete("app") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + ; + + // Delete the algorithm + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("algorithmid", algId) + .when() + .redirects().follow(true) + .redirects().max(3) + .delete("algorithm") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NO_CONTENT)) + ; + + // Delete the site + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("siteid", siteId) + .when() + .redirects().follow(true) + .redirects().max(3) + .delete("site") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + ; + + logout(sessionFilter); + } + + @TestTemplate + void testGetComputationRefs() + { + JsonPath expectedJson = getJsonPathFromResource("computation_refs_expected.json"); + + ExtractableResponse response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("computationrefs") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .extract() + ; + List> actualList = response.body().jsonPath().getList(""); + assertFalse(actualList.isEmpty()); + Map actualItem = actualList.get(0); + assertEquals(expectedJson.getString("name"), actualItem.get("name")); + assertEquals(expectedJson.getString("description"), actualItem.get("description")); + assertEquals(expectedJson.getBoolean("enabled"), actualItem.get("enabled")); + assertEquals(expectedJson.getString("groupName"), actualItem.get("groupName")); + assertEquals(expectedJson.getString("applicationName"), actualItem.get("applicationName")); + assertEquals(expectedJson.getString("algorithmName"), actualItem.get("algorithmName")); + assertEquals(expectedJson.getString("parmList[0].siteName"), + ((Map) (((List) actualItem.get("parmList")).get(0))).get("siteName")); + assertEquals(expectedJson.getString("parmList[0].dataType"), + ((Map) (((List) actualItem.get("parmList")).get(0))).get("dataType")); + assertEquals(expectedJson.getString("parmList[0].interval"), + ((Map) (((List) actualItem.get("parmList")).get(0))).get("interval")); + assertEquals(expectedJson.getString("comment"), actualItem.get("comment")); + } + + @TestTemplate + void testGetComputationRefsWithFilters() throws Exception + { + ApiComputation expectedComp = getDtoFromResource("computation_refs_expected.json", + ApiComputation.class); + JsonPath expected = getJsonPathFromResource("computation_refs_expected.json"); + + ExtractableResponse response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("site", expectedComp.getParmList().get(0).getSiteName()) + .queryParam("datatype", expectedComp.getParmList().get(0).getDataType()) + .queryParam("group", expectedComp.getGroupName()) + .queryParam("algorithm", expectedComp.getAlgorithmName()) + .queryParam("process", expectedComp.getApplicationName()) + .queryParam("enabled", expectedComp.isEnabled()) + .queryParam("interval", expectedComp.getParmList().get(0).getInterval()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("computationrefs") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .extract() + ; + + List> actualList = response.body().jsonPath().getList(""); + assertFalse(actualList.isEmpty()); + Map actualItem = actualList.get(0); + assertEquals(expected.getString("name"), actualItem.get("name")); + assertEquals(expected.getString("description"), actualItem.get("description")); + assertEquals(expected.getBoolean("enabled"), actualItem.get("enabled")); + assertEquals(expected.getString("groupName"), actualItem.get("groupName")); + assertEquals(expected.getString("applicationName"), actualItem.get("applicationName")); + assertEquals(expected.getString("algorithmName"), actualItem.get("algorithmName")); + assertEquals(expected.getString("parmList[0].siteName"), + ((Map) (((List) actualItem.get("parmList")).get(0))).get("siteName")); + assertEquals(expected.getString("parmList[0].dataType"), + ((Map) (((List) actualItem.get("parmList")).get(0))).get("dataType")); + assertEquals(expected.getString("parmList[0].interval"), + ((Map) (((List) actualItem.get("parmList")).get(0))).get("interval")); + assertEquals(expected.getString("comment"), actualItem.get("comment")); + } + + @TestTemplate + void testGetComputationRefsWithNoMatchingFilters() throws Exception + { + ApiComputation expectedComp = getDtoFromResource("computation_refs_expected.json", + ApiComputation.class); + + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("site", expectedComp.getParmList().get(0).getSiteName()) + .queryParam("datatype", expectedComp.getParmList().get(0).getDataType()) + .queryParam("group", "test group") + .queryParam("algorithm", expectedComp.getAlgorithmName()) + .queryParam("process", expectedComp.getApplicationName()) + .queryParam("enabled", false) + .queryParam("interval", expectedComp.getParmList().get(0).getInterval()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("computationrefs") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NOT_FOUND)) + ; + + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("site", expectedComp.getParmList().get(0).getSiteName()) + .queryParam("datatype", expectedComp.getParmList().get(0).getDataType()) + .queryParam("group", expectedComp.getGroupName()) + .queryParam("algorithm", expectedComp.getAlgorithmName()) + .queryParam("process", expectedComp.getApplicationName()) + .queryParam("enabled", expectedComp.isEnabled()) + .queryParam("interval", "bi-annual") + .when() + .redirects().follow(true) + .redirects().max(3) + .get("computationrefs") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NOT_FOUND)) + ; + + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("site", expectedComp.getParmList().get(0).getSiteName()) + .queryParam("datatype", expectedComp.getParmList().get(0).getDataType()) + .queryParam("group", expectedComp.getGroupName()) + .queryParam("algorithm", "water pressure calc") + .queryParam("process", expectedComp.getApplicationName()) + .queryParam("enabled", expectedComp.isEnabled()) + .queryParam("interval", expectedComp.getParmList().get(0).getInterval()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("computationrefs") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NOT_FOUND)) + ; + } + + @TestTemplate + void testGetComputation() + { + JsonPath expected = getJsonPathFromResource("computation_insert_data.json"); + + ExtractableResponse response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("computationid", compId) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("computation") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .extract() + ; + + JsonPath actual = response.body().jsonPath(); + assertEquals(expected.getString("name"), actual.getString("name")); + assertEquals(expected.getString("description"), actual.getString("description")); + assertEquals(expected.getString("enabled"), actual.getString("enabled")); + assertEquals(expected.getString("groupName"), actual.getString("groupName")); + assertEquals(expected.getString("applicationName"), actual.getString("applicationName")); + assertEquals("Test Algorithm", actual.getString("algorithmName")); + assertEquals(algId, actual.getLong("algorithmId")); + assertEquals(appId, actual.getLong("appId")); + assertEquals(siteId, actual.getLong("parmList[0].siteId")); + assertEquals(expected.getString("parmList[0].siteName"), actual.getString("parmList[0].siteName")); + assertEquals(expected.getString("parmList[0].dataType"), actual.getString("parmList[0].dataType")); + assertEquals(expected.getString("parmList[0].interval"), actual.getString("parmList[0].interval")); + assertEquals(expected.getString("comment"), actual.getString("comment")); + } + + @TestTemplate + void testPostAndDeleteComputation() throws Exception + { + ApiComputation comp = getDtoFromResource("computation_post_delete_insert_data.json", + ApiComputation.class); + + String compJson = MAPPER.writeValueAsString(comp); + + // Store the new computation + ExtractableResponse response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .body(compJson) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("computation") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)) + .extract() + ; + + Long newCompId = response.body().jsonPath().getLong("computationId"); + + // Get the new computation and assert it matches the expected data + response = given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("computationid", newCompId) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("computation") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .extract() + ; + + JsonPath actual = response.body().jsonPath(); + assertEquals(comp.getName(), actual.getString("name")); + assertEquals(comp.isEnabled(), actual.getBoolean("enabled")); + assertEquals(comp.getGroupName(), actual.getString("groupName")); + assertEquals(comp.getApplicationName(), actual.getString("applicationName")); + assertEquals(comp.getAlgorithmName(), actual.getString("algorithmName")); + assertEquals(comp.getComment(), actual.getString("comment")); + List> actualParmList = actual.getList("parmList"); + int i = 0; + for (ApiCompParm parm : comp.getParmList()) + { + Map actualItem = actualParmList.get(i); + assertEquals(parm.getSiteName(), actualItem.get("siteName")); + assertEquals(parm.getDataType(), actualItem.get("dataType")); + assertEquals(parm.getInterval(), actualItem.get("interval")); + i++; + } + + // Delete the new computation + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("computationid", newCompId) + .when() + .redirects().follow(true) + .redirects().max(3) + .delete("computation") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NO_CONTENT)) + ; + + // Get the new computation and assert it is not found + given() + .log().ifValidationFails(LogDetail.ALL, true) + .filter(sessionFilter) + .accept(MediaType.APPLICATION_JSON) + .header("Authorization", authHeader) + .queryParam("computationid", newCompId) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("computation") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NOT_FOUND)) + ; + } +} diff --git a/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_algorithm_data.json b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_algorithm_data.json new file mode 100644 index 000000000..9ded8954c --- /dev/null +++ b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_algorithm_data.json @@ -0,0 +1,12 @@ +{ + "algorithmId" : null, + "name" : "Test Algorithm", + "execClass" : null, + "description" : "Test Algorithm Description", + "props" : { + "prop1" : "value1" + }, + "parms" : [ ], + "numCompsUsing" : 0, + "algoScripts" : [ ] +} \ No newline at end of file diff --git a/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_app_data.json b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_app_data.json new file mode 100644 index 000000000..13a7eae2c --- /dev/null +++ b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_app_data.json @@ -0,0 +1,12 @@ + +{ + "appId" : null, + "appName" : "Computational App", + "appType" : "Computation", + "comment" : "Performs computations", + "lastModified" : null, + "manualEditingApp" : true, + "properties" : { + "prop1" : "value1" + } +} \ No newline at end of file diff --git a/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_insert_data.json b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_insert_data.json new file mode 100644 index 000000000..a8c85e1bd --- /dev/null +++ b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_insert_data.json @@ -0,0 +1,46 @@ +{ + "computationId" : null, + "name" : "Flow Computation", + "comment" : "Performs flow computations", + "appId" : null, + "applicationName" : null, + "lastModified" : 1736199844670, + "enabled" : true, + "effectiveStartType" : "No Limit", + "effectiveStartDate" : 1736199844670, + "effectiveStartInterval" : "daily", + "effectiveEndType" : "No Limit", + "effectiveEndDate" : 1736199844670, + "effectiveEndInterval" : "hourly", + "algorithmId" : null, + "algorithmName" : null, + "parmList" : [ { + "algoParmType" : "Numeric", + "algoRoleName" : "input", + "tsKey" : null, + "dataTypeId" : null, + "dataType" : null, + "interval" : "hourly", + "deltaT" : 2, + "deltaTUnits" : "Seconds", + "unitsAbbr" : "cfs", + "siteId" : null, + "siteName" : null, + "tableSelector" : null, + "modelId" : null, + "paramType" : null, + "duration" : "1 week", + "version" : "1.1.0", + "ifMissing" : "Do not compute" + } ], + "props" : { + "computationType": "Flow", + "computationMethod": "Flow", + "computationUnits": "cfs", + "computationDuration": "1 week", + "computationVersion": "1.1.0", + "computationIfMissing": "Do not compute" + }, + "groupId" : null, + "groupName" : null +} \ No newline at end of file diff --git a/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_post_delete_insert_data.json b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_post_delete_insert_data.json new file mode 100644 index 000000000..a98e324fb --- /dev/null +++ b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_post_delete_insert_data.json @@ -0,0 +1,46 @@ +{ + "computationId" : null, + "name" : "Depth Computation", + "comment" : "Performs depth computations", + "appId" : null, + "applicationName" : null, + "lastModified" : 1736199844670, + "enabled" : true, + "effectiveStartType" : "No Limit", + "effectiveStartDate" : 1736199844670, + "effectiveStartInterval" : "hourly", + "effectiveEndType" : "No Limit", + "effectiveEndDate" : 1736199844670, + "effectiveEndInterval" : "monthly", + "algorithmId" : null, + "algorithmName" : null, + "parmList" : [ { + "algoParmType" : "Numeric", + "algoRoleName" : "input", + "tsKey" : null, + "dataTypeId" : null, + "dataType" : null, + "interval" : "annual", + "deltaT" : 2, + "deltaTUnits" : "Seconds", + "unitsAbbr" : "cms", + "siteId" : null, + "siteName" : null, + "tableSelector" : null, + "modelId" : null, + "paramType" : null, + "duration" : "1 month", + "version" : "1.2.0", + "ifMissing" : "Do not compute" + } ], + "props" : { + "computationType": "Depth", + "computationMethod": "Depth", + "computationUnits": "cms", + "computationDuration": "1 month", + "computationVersion": "1.2.0", + "computationIfMissing": "Do not compute" + }, + "groupId" : null, + "groupName" : null +} \ No newline at end of file diff --git a/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_refs_expected.json b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_refs_expected.json new file mode 100644 index 000000000..9c79b2db5 --- /dev/null +++ b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_refs_expected.json @@ -0,0 +1,48 @@ +{ + "computationId": null, + "name": "Flow Computation", + "comment": "Performs flow computations", + "appId": -1, + "applicationName": "Computational App", + "lastModified": "2025-01-06T22:27:25.554Z[UTC]", + "enabled": true, + "effectiveStartType": "No Limit", + "effectiveStartDate": "2025-01-06T21:44:04.670Z[UTC]", + "effectiveStartInterval": null, + "effectiveEndType": "No Limit", + "effectiveEndDate": "2025-01-06T21:44:04.670Z[UTC]", + "effectiveEndInterval": null, + "algorithmId": -1, + "algorithmName": "Test Algorithm", + "parmList": [ + { + "algoParmType": null, + "algoRoleName": "input", + "tsKey": null, + "dataTypeId": null, + "dataType": null, + "interval": "hourly", + "deltaT": 2, + "deltaTUnits": "Seconds", + "unitsAbbr": null, + "siteId": -1, + "siteName": null, + "tableSelector": "", + "modelId": -1, + "paramType": "", + "duration": "1 month", + "version": "1.2.3", + "ifMissing": null + } + ], + "props": { + "computationType": "Flow", + "computationMethod": "Flow", + "computationUnits": "cfs", + "computationDuration": "1 week", + "computationVersion": "1.1.0", + "computationIfMissing": "Do not compute" + }, + "groupId": -1, + "groupName": null +} \ No newline at end of file diff --git a/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_site_data.json b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_site_data.json new file mode 100644 index 000000000..46c4e34ea --- /dev/null +++ b/opendcs-rest-api/src/test/resources/org/opendcs/odcsapi/res/it/OPEN_TSDB/computation_site_data.json @@ -0,0 +1,24 @@ +{ + "siteId" : null, + "sitenames" : { + "Site" : "Test Site" + }, + "description" : "A site for computations", + "latitude" : "131.6", + "longitude" : "-78.4", + "elevation" : 25.0, + "elevUnits" : "m", + "nearestCity" : "Lincoln", + "timezone" : "CST", + "state" : "NE", + "country" : "US", + "region" : "Midwest", + "active" : true, + "locationType" : "Site", + "publicName" : "Test Site", + "properties" : { + "siteRainfall" : "13.0", + "siteRainfallUnits" : "in" + }, + "lastModified" : null +} \ No newline at end of file