Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -124,6 +124,12 @@ public void create(@NotNull Context ctx) {
LocationLevel level = deserializeLocationLevel(ctx);
level.validate();

if (!LocationLevel.truncateDate(level.getLevelDate()).equals(level.getLevelDate())) {
Map<String, String> errorDetails = new HashMap<>();
errorDetails.put("message", "Level effective date cannot have seconds");
throw new BadRequestResponse("", errorDetails);
}

DSLContext dsl = getDslContext(ctx);
LocationLevelsDao levelsDao = getLevelsDao(dsl);
levelsDao.storeLocationLevel(level);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@

package cwms.cda.data.dto.locationlevel;

import cwms.cda.data.dto.catalog.LocationAlias;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
Expand All @@ -47,13 +39,23 @@
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import cwms.cda.data.dto.CwmsDTO;
import cwms.cda.data.dto.CwmsDTOValidator;
import cwms.cda.data.dto.catalog.LocationAlias;
import cwms.cda.formatters.Formats;
import cwms.cda.formatters.UnsupportedFormatException;
import cwms.cda.formatters.annotations.FormattableWith;
import cwms.cda.formatters.json.JsonV1;
import cwms.cda.formatters.json.JsonV2;
import cwms.cda.formatters.xml.XMLv2;
import io.swagger.v3.oas.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

@JsonRootName("LocationLevel")
@JsonDeserialize(builder = LocationLevel.Builder.class)
Expand Down Expand Up @@ -95,7 +97,8 @@ public abstract class LocationLevel extends CwmsDTO {
@Schema(description = "Units the provided levels are in")

private final String levelUnitsId;
@Schema(description = "The date/time at which this location level configuration takes effect.")
@Schema(description = "The date/time at which this location level configuration takes effect. "
+ "Must be limited to minute precision.")
@JsonFormat(shape = JsonFormat.Shape.STRING)

private final Instant levelDate;
Expand Down Expand Up @@ -501,4 +504,12 @@ protected void validateInternal(CwmsDTOValidator validator) {
validator.required(getLocationLevelId(), "location-level-id");
validator.required(getLevelDate(), "level-date");
}

public static ZonedDateTime truncateDate(ZonedDateTime date) {
return date.truncatedTo(ChronoUnit.MINUTES);
}

public static Instant truncateDate(Instant date) {
return date.truncatedTo(ChronoUnit.MINUTES);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"constant-value": 10,
"level-units-id": "ft",
"level-date": "2008-12-03T10:15:30+01:00[UTC]",
"level-date": "2008-12-03T10:15:00+01:00[UTC]",
"level-comment": "Lowest Point",
"interpolate-string": "F",
"location-level-id": "Sacramento.Elev.Inst.0.Bottom of Inlet",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
}
],
"level-units-id": "ft",
"level-date": "2008-12-03T10:15:30+01:00[UTC]",
"level-date": "2008-12-03T10:15:00+01:00[UTC]",
"level-comment": "Lowest Point",
"interval-origin": "2008-12-03T10:15:30+01:00[UTC]",
"interval-origin": "2008-12-03T10:15:00+01:00[UTC]",
"interval-months": 1,
"interpolate-string": "F",
"location-level-id": "Sacramento.Elev.Inst.0.Bottom of Inlet",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"seasonal-time-series-id": "Sacramento.Stage.Inst.5Minutes.0.Bottom of Inlet",
"level-units-id": "ft",
"level-date": "2008-12-03T10:15:30+01:00[UTC]",
"level-date": "2008-12-03T10:15:00+01:00[UTC]",
"level-comment": "Bottom of Inlet, lowest level",
"interpolate-string": "F",
"location-level-id": "Sacramento.Elev.Inst.0.Bottom of Inlet",
Expand Down
142 changes: 114 additions & 28 deletions cwms-data-api/src/test/java/cwms/cda/api/LevelsControllerTestIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,33 @@

package cwms.cda.api;

import static cwms.cda.api.Controllers.BEGIN;
import static cwms.cda.api.Controllers.EFFECTIVE_DATE;
import static cwms.cda.api.Controllers.EFFECTIVE_DATE_EXACT;
import static cwms.cda.api.Controllers.END;
import static cwms.cda.api.Controllers.FORMAT;
import static cwms.cda.api.Controllers.INCLUDE_ALIASES;
import static cwms.cda.api.Controllers.INTERVAL;
import static cwms.cda.api.Controllers.LEVEL_ID_MASK;
import static cwms.cda.api.Controllers.PAGE;
import static cwms.cda.api.Controllers.PAGE_SIZE;
import static cwms.cda.api.Controllers.START;
import static cwms.cda.api.Controllers.UNIT;
import static cwms.cda.security.ApiKeyIdentityProvider.AUTH_HEADER;
import static helpers.FloatCloseTo.floatCloseTo;
import static io.restassured.RestAssured.given;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isOneOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import cwms.cda.ApiServlet;
import cwms.cda.data.dao.LocationCategoryDao;
import cwms.cda.data.dao.LocationGroupDao;
Expand All @@ -33,9 +60,9 @@
import cwms.cda.data.dto.AssignedLocation;
import cwms.cda.data.dto.LocationCategory;
import cwms.cda.data.dto.LocationGroup;
import cwms.cda.data.dto.TimeSeries;
import cwms.cda.data.dto.locationlevel.ConstantLocationLevel;
import cwms.cda.data.dto.locationlevel.LocationLevel;
import cwms.cda.data.dto.TimeSeries;
import cwms.cda.data.dto.locationlevel.SeasonalLocationLevel;
import cwms.cda.data.dto.locationlevel.SeasonalValueBean;
import cwms.cda.data.dto.locationlevel.TimeSeriesLocationLevel;
Expand All @@ -44,11 +71,24 @@
import fixtures.CwmsDataApiSetupCallback;
import fixtures.MinimumSchema;
import fixtures.TestAccounts;
import hec.data.RatingException;
import hec.data.cwmsRating.io.RatingSetContainer;
import hec.data.cwmsRating.io.RatingSpecContainer;
import io.restassured.filter.log.LogDetail;

import io.restassured.path.json.JsonPath;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import java.io.IOException;
import java.sql.SQLException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletResponse;
import mil.army.usace.hec.cwms.rating.io.xml.RatingContainerXmlFactory;
import mil.army.usace.hec.cwms.rating.io.xml.RatingSetContainerXmlFactory;
import mil.army.usace.hec.cwms.rating.io.xml.RatingSpecXmlFactory;
Expand All @@ -61,32 +101,6 @@
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;

import hec.data.RatingException;
import hec.data.cwmsRating.io.RatingSetContainer;
import hec.data.cwmsRating.io.RatingSpecContainer;

import static cwms.cda.api.Controllers.*;
import static cwms.cda.security.ApiKeyIdentityProvider.AUTH_HEADER;
import static helpers.FloatCloseTo.floatCloseTo;
import static io.restassured.RestAssured.given;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

@Tag("integration")
public class LevelsControllerTestIT extends DataApiTestIT {

Expand Down Expand Up @@ -2081,6 +2095,42 @@ void testStoreConstantLevel() throws Exception {
.body("constant-value", equalTo(8675.309f));
}

@Test
void testStoreTimeRestrictedLevel() throws Exception {
String locName = "restrictedLoc123";
createLocation(locName, true, OFFICE);
String levelId = String.format("%s.Elev.Ave.1Day.Regulating", locName);
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.parse("2024-01-01T00:00:12Z"), ZoneId.of("UTC"));
ConstantLocationLevel level = new ConstantLocationLevel.Builder(levelId, time.toInstant())
.withOfficeId(OFFICE)
.withLevelUnitsId("ft")
.withConstantValue(8675.309)
.withExpirationDate(time.plusYears(50).toInstant())
.build();

String levelJson = Formats.format(new ContentType(Formats.JSONV2), level);

given()
.log().ifValidationFails(LogDetail.ALL, true)
.queryParam(Controllers.OFFICE, OFFICE)
.header("Authorization", TestAccounts.KeyUser.SPK_NORMAL.toHeaderValue())
.body(levelJson)
.contentType(Formats.JSONV2)
.when()
.redirects()
.follow(true)
.redirects()
.max(3)
.post("/levels/")
.then()
.log().ifValidationFails(LogDetail.ALL, true)
.assertThat()
.statusCode(is(HttpServletResponse.SC_BAD_REQUEST))
.body("details.message", equalTo("Level effective date cannot have seconds"))
.body("source", equalTo("User Input"))
.body(MESSAGE, equalTo("Bad Request"));
}

enum GetAllTestLegacy {
JSON(Formats.JSON_LEGACY, Formats.JSON),
XML(Formats.XML_LEGACY, Formats.XML),
Expand Down Expand Up @@ -2414,6 +2464,42 @@ void test_get_aliases() throws Exception {
;
}

@Test
void test_create_subminute_level() throws Exception {
String locName = "subminuteloc";
createLocation(locName, true, OFFICE);
String levelId = String.format("%s.Elev.Ave.1Day.Top of Inlet", locName);
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.parse("2024-01-01T00:00:25Z"), ZoneId.of("UTC"));
ConstantLocationLevel level = new ConstantLocationLevel.Builder(levelId, time.toInstant())
.withOfficeId(OFFICE)
.withLevelUnitsId("ft")
.withConstantValue(8675.309)
.withExpirationDate(time.toInstant())
.build();

String levelJson = Formats.format(new ContentType(Formats.JSONV2), level);

given()
.log().ifValidationFails(LogDetail.ALL, true)
.queryParam(Controllers.OFFICE, OFFICE)
.header("Authorization", TestAccounts.KeyUser.SPK_NORMAL.toHeaderValue())
.body(levelJson)
.contentType(Formats.JSONV2)
.when()
.redirects()
.follow(true)
.redirects()
.max(3)
.post("/levels/")
.then()
.log().ifValidationFails(LogDetail.ALL, true)
.assertThat()
.statusCode(is(HttpServletResponse.SC_BAD_REQUEST))
.body("details.message", is("Level effective date cannot have seconds"))
.body("message", is("Bad Request"))
.body("source", is("User Input"));
}

enum GetAllTestNewAliases {
DEFAULT(Formats.DEFAULT, Formats.JSONV2),
JSON(Formats.JSON, Formats.JSONV2),
Expand Down
16 changes: 8 additions & 8 deletions cwms-data-api/src/test/java/cwms/cda/api/LockControllerIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;

import com.google.common.flogger.FluentLogger;
import cwms.cda.api.enums.Nation;
import cwms.cda.api.errors.NotFoundException;
import cwms.cda.data.dao.DeleteRule;
Expand All @@ -45,10 +46,10 @@
import cwms.cda.data.dao.location.kind.LockDao;
import cwms.cda.data.dto.CwmsId;
import cwms.cda.data.dto.Location;
import cwms.cda.data.dto.locationlevel.ConstantLocationLevel;
import cwms.cda.data.dto.locationlevel.LocationLevel;
import cwms.cda.data.dto.location.kind.Lock;
import cwms.cda.data.dto.location.kind.LockLocationLevelRef;
import cwms.cda.data.dto.locationlevel.ConstantLocationLevel;
import cwms.cda.data.dto.locationlevel.LocationLevel;
import cwms.cda.formatters.ContentType;
import cwms.cda.formatters.Formats;
import fixtures.CwmsDataApiSetupCallback;
Expand All @@ -65,7 +66,6 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import com.google.common.flogger.FluentLogger;
import javax.servlet.http.HttpServletResponse;
import mil.army.usace.hec.test.database.CwmsDatabaseContainer;
import org.apache.commons.io.IOUtils;
Expand Down Expand Up @@ -1049,36 +1049,36 @@ private static PROJECT_OBJ_T buildProject(Location projectLocation) {

private List<LocationLevel> createLocationLevelList(Lock lock) {
List<LocationLevel> retVal = new ArrayList<>();
var lowLowerLevel = new ConstantLocationLevel.Builder(lock.getLowWaterLowerPoolLocationLevel().getLevelId(), ZonedDateTime.now().toInstant())
var lowLowerLevel = new ConstantLocationLevel.Builder(lock.getLowWaterLowerPoolLocationLevel().getLevelId(), LocationLevel.truncateDate(ZonedDateTime.now()).toInstant())
.withLevelUnitsId(lock.getElevationUnits())
.withOfficeId(lock.getLowWaterLowerPoolLocationLevel().getOfficeId())
.withSpecifiedLevelId(lock.getLowWaterLowerPoolLocationLevel().getSpecifiedLevelId())
.withConstantValue(lock.getLowWaterLowerPoolLocationLevel().getLevelValue())
.build();
retVal.add(lowLowerLevel);
var lowUpperLevel = new ConstantLocationLevel.Builder(lock.getLowWaterUpperPoolLocationLevel().getLevelId(), ZonedDateTime.now().toInstant())
var lowUpperLevel = new ConstantLocationLevel.Builder(lock.getLowWaterUpperPoolLocationLevel().getLevelId(), LocationLevel.truncateDate(ZonedDateTime.now()).toInstant())
.withLevelUnitsId(lock.getElevationUnits())
.withOfficeId(lock.getLowWaterUpperPoolLocationLevel().getOfficeId())
.withSpecifiedLevelId(lock.getLowWaterUpperPoolLocationLevel().getSpecifiedLevelId())
.withConstantValue(lock.getLowWaterUpperPoolLocationLevel().getLevelValue())
.build();
retVal.add(lowUpperLevel);
var highLowerLevel = new ConstantLocationLevel.Builder(lock.getHighWaterLowerPoolLocationLevel().getLevelId(), ZonedDateTime.now().toInstant())
var highLowerLevel = new ConstantLocationLevel.Builder(lock.getHighWaterLowerPoolLocationLevel().getLevelId(), LocationLevel.truncateDate(ZonedDateTime.now()).toInstant())
.withLevelUnitsId(lock.getElevationUnits())
.withOfficeId(lock.getHighWaterLowerPoolLocationLevel().getOfficeId())
.withSpecifiedLevelId(lock.getHighWaterLowerPoolLocationLevel().getSpecifiedLevelId())
.withConstantValue(lock.getHighWaterLowerPoolLocationLevel().getLevelValue())
.build();
retVal.add(highLowerLevel);
var highUpperLevel = new ConstantLocationLevel.Builder(lock.getHighWaterUpperPoolLocationLevel().getLevelId(), ZonedDateTime.now().toInstant())
var highUpperLevel = new ConstantLocationLevel.Builder(lock.getHighWaterUpperPoolLocationLevel().getLevelId(), LocationLevel.truncateDate(ZonedDateTime.now()).toInstant())
.withLevelUnitsId(lock.getElevationUnits())

.withOfficeId(lock.getHighWaterUpperPoolLocationLevel().getOfficeId())
.withSpecifiedLevelId(lock.getHighWaterUpperPoolLocationLevel().getSpecifiedLevelId())
.withConstantValue(lock.getHighWaterUpperPoolLocationLevel().getLevelValue())
.build();
retVal.add(highUpperLevel);
var warningBuffer = new ConstantLocationLevel.Builder(String.format("%s.Elev-Closure.Inst.0.Warning Buffer", lock.getLocation().getName()), ZonedDateTime.now().toInstant())
var warningBuffer = new ConstantLocationLevel.Builder(String.format("%s.Elev-Closure.Inst.0.Warning Buffer", lock.getLocation().getName()), LocationLevel.truncateDate(ZonedDateTime.now()).toInstant())
.withLevelUnitsId(lock.getElevationUnits())
.withOfficeId(lock.getLocation().getOfficeId())
.withSpecifiedLevelId("Warning Buffer")
Expand Down
Loading