From 074ae0a83a513452667a750bd19b34f22a86a359 Mon Sep 17 00:00:00 2001 From: Adam Korynta Date: Mon, 3 Jun 2024 15:03:42 -0700 Subject: [PATCH 1/9] fixing jaxb serialization of location --- .../main/java/cwms/cda/data/dto/Location.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java index 3450297bd..bb28f73ae 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java @@ -35,29 +35,54 @@ @FormattableWith(contentType = Formats.JSON, formatter = JsonV1.class) public final class Location extends CwmsDTO { @JsonProperty(required = true) + @XmlElement(name = "name", required = true) private final String name; + @XmlElement(name = "latitude") private final Double latitude; + @XmlElement(name = "longitude") private final Double longitude; + @XmlElement(name = "active") private final Boolean active; + @XmlElement(name = "public-name") private final String publicName; + @XmlElement(name = "long-name") private final String longName; + @XmlElement(name = "description") private final String description; + @XmlElement(name = "timezone-name") private final String timezoneName; + @XmlElement(name = "location-type") private final String locationType; + @XmlElement(name = "location-kind") private final String locationKind; + @XmlElement(name = "nation") private final Nation nation; + @XmlElement(name = "state-initial") private final String stateInitial; + @XmlElement(name = "county-name") private final String countyName; + @XmlElement(name = "nearest-city") private final String nearestCity; + @XmlElement(name = "horizontal-datum") private final String horizontalDatum; + @XmlElement(name = "published-longitude") private final Double publishedLongitude; + @XmlElement(name = "published-latitude") private final Double publishedLatitude; + @XmlElement(name = "vertical-datum") private final String verticalDatum; + @XmlElement(name = "elevation") private final Double elevation; + @XmlElement(name = "map-label") private final String mapLabel; + @XmlElement(name = "bounding-office-id") private final String boundingOfficeId; private final String elevationUnits; + private Location() { + this(new Builder(null, null, null, null, null,null, null)); + } + private Location(Builder builder) { super(builder.officeId); this.name = builder.name; From 9cd476784c3705e0b60e1cfdbe4837a559d86316 Mon Sep 17 00:00:00 2001 From: Adam Korynta Date: Tue, 4 Jun 2024 16:54:07 -0700 Subject: [PATCH 2/9] update all DTO's to use jackson instead of jaxb --- .../main/java/cwms/cda/data/dto/Location.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java index bb28f73ae..64a02dac9 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java @@ -35,47 +35,26 @@ @FormattableWith(contentType = Formats.JSON, formatter = JsonV1.class) public final class Location extends CwmsDTO { @JsonProperty(required = true) - @XmlElement(name = "name", required = true) private final String name; - @XmlElement(name = "latitude") private final Double latitude; - @XmlElement(name = "longitude") private final Double longitude; - @XmlElement(name = "active") private final Boolean active; - @XmlElement(name = "public-name") private final String publicName; - @XmlElement(name = "long-name") private final String longName; - @XmlElement(name = "description") private final String description; - @XmlElement(name = "timezone-name") private final String timezoneName; - @XmlElement(name = "location-type") private final String locationType; - @XmlElement(name = "location-kind") private final String locationKind; - @XmlElement(name = "nation") private final Nation nation; - @XmlElement(name = "state-initial") private final String stateInitial; - @XmlElement(name = "county-name") private final String countyName; - @XmlElement(name = "nearest-city") private final String nearestCity; - @XmlElement(name = "horizontal-datum") private final String horizontalDatum; - @XmlElement(name = "published-longitude") private final Double publishedLongitude; - @XmlElement(name = "published-latitude") private final Double publishedLatitude; - @XmlElement(name = "vertical-datum") private final String verticalDatum; - @XmlElement(name = "elevation") private final Double elevation; - @XmlElement(name = "map-label") private final String mapLabel; - @XmlElement(name = "bounding-office-id") private final String boundingOfficeId; private final String elevationUnits; From 3ac49fed5856f661c42fb315733e75db60db7c03 Mon Sep 17 00:00:00 2001 From: rma-rripken <89810919+rma-rripken@users.noreply.github.com> Date: Thu, 6 Jun 2024 18:41:32 -0700 Subject: [PATCH 3/9] Adding Lock and Lock Rights --- .../java/cwms/cda/data/dao/ProjectDao.java | 233 +++++++++++++++++- 1 file changed, 231 insertions(+), 2 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java index 955c9f461..c63e29b97 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java @@ -11,6 +11,7 @@ import cwms.cda.data.dto.Projects; import cwms.cda.helpers.ResourceHelper; import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,8 +26,10 @@ import org.jooq.Condition; import org.jooq.DSLContext; import org.jooq.Record1; +import org.jooq.SQLDialect; import org.jooq.SelectConditionStep; import org.jooq.SelectLimitPercentStep; +import org.jooq.impl.DSL; import usace.cwms.db.dao.ifc.loc.LocationRefType; import usace.cwms.db.dao.ifc.loc.LocationType; import usace.cwms.db.dao.util.OracleTypeMap; @@ -123,7 +126,8 @@ private Project buildProject(PROJECT_OBJ_T projectObjT) { } if (nearLocRef != null) { builder = builder.withNearGageLocation(new Location.Builder(nearLocRef.getOfficeId(), - getLocationId(nearLocRef.getBaseLocationId(), nearLocRef.getSubLocationId())) + getLocationId(nearLocRef.getBaseLocationId(), + nearLocRef.getSubLocationId())) .withActive(null) .build() ); @@ -137,7 +141,8 @@ private Project buildProject(PROJECT_OBJ_T projectObjT) { } if (pumpbackLocRef != null) { builder = builder.withPumpBackLocation(new Location.Builder(pumpbackLocRef.getOfficeId(), - getLocationId(pumpbackLocRef.getBaseLocationId(), pumpbackLocRef.getSubLocationId())) + getLocationId(pumpbackLocRef.getBaseLocationId(), + pumpbackLocRef.getSubLocationId())) .withActive(null) .build() ); @@ -568,4 +573,228 @@ public void delete(String office, String id, DeleteRule deleteRule) { id, deleteRule.getRule(), office )); } + + + public Number publishStatusUpdate(String pProjectId, + String appId, String sourceId, + String tsId, Timestamp start, + Timestamp end, String office) { + BigInteger startTime = toBigInteger(start); + BigInteger endTime = toBigInteger(end); + return connectionResult(dsl, c -> CWMS_PROJECT_PACKAGE.call_PUBLISH_STATUS_UPDATE( + getDslContext(c, office).configuration(), + pProjectId, appId, sourceId, + tsId, startTime, endTime, office) + ); + } + + protected static BigInteger toBigInteger(Timestamp timestamp) { + BigInteger retval = null; + if (timestamp != null) { + retval = BigInteger.valueOf(timestamp.getTime()); + } + + return retval; + } + + protected static BigInteger toBigInteger(Long value) { + BigInteger retval = null; + if (value != null) { + retval = BigInteger.valueOf(value); + } + + return retval; + } + + protected static BigInteger toBigInteger(int revokeTimeout) { + return BigInteger.valueOf(revokeTimeout); + } + + + public String requestLock(String projectId, String appId, + boolean revokeExisting, int revokeTimeout, String office) { + BigInteger revokeTimeoutBI = toBigInteger(revokeTimeout); + return connectionResult(dsl, c -> CWMS_PROJECT_PACKAGE.call_REQUEST_LOCK(getDslContext(c, + office).configuration(), + projectId, appId, OracleTypeMap.formatBool(revokeExisting), revokeTimeoutBI, + office)); + } + + + public void releaseLock(String lockId) { + connection(dsl, c -> CWMS_PROJECT_PACKAGE.call_RELEASE_LOCK( + DSL.using(c, SQLDialect.ORACLE18C).configuration(), + lockId)); + } + + + public void revokeLock(String projId, String appId, + int revokeTimeout, String office) { + BigInteger revokeTimeoutBI = toBigInteger(revokeTimeout); + connection(dsl, c -> + CWMS_PROJECT_PACKAGE.call_REVOKE_LOCK(getDslContext(c, office).configuration(), + projId, + appId, revokeTimeoutBI, office)); + } + + + public void denyLockRevocation(String lockId) { + connection(dsl, c -> + CWMS_PROJECT_PACKAGE.call_DENY_LOCK_REVOCATION( + DSL.using(c, SQLDialect.ORACLE18C).configuration(), + lockId)); + } + + + public boolean isLocked(String projectId, String appId, String office) { + String s = connectionResult(dsl, c -> CWMS_PROJECT_PACKAGE.call_IS_LOCKED(getDslContext(c + , office).configuration(), + projectId, appId, office)); + return OracleTypeMap.parseBool(s); + } + + + public List catLocks(String projMask, String appMask, TimeZone tz, String officeMask) throws SQLException { + List retval = new ArrayList<>(); + try (ResultSet resultSet = CWMS_PROJECT_PACKAGE.call_CAT_LOCKS(dsl.configuration(), + projMask, appMask, tz.getID(), officeMask) + .intoResultSet()) { + while (resultSet.next()) { + String officeId = resultSet.getString("office_id"); + String projectId = resultSet.getString("project_id"); + String applicationId = resultSet.getString("application_id"); + String acquireTime = resultSet.getString("acquire_time"); + String sessionUser = resultSet.getString("session_user"); + String osUser = resultSet.getString("os_user"); + String sessionProgram = resultSet.getString("session_program"); + String sessionMachine = resultSet.getString("session_machine"); + + Lock lock = new Lock(officeId, projectId, applicationId, acquireTime, sessionUser + , osUser, sessionProgram, sessionMachine); + retval.add(lock); + } + } + return retval; + } + + public static class Lock { + private final String officeId; + private final String projectId; + private final String applicationId; + private final String acquireTime; + private final String sessionUser; + private final String osUser; + private final String sessionProgram; + private final String sessionMachine; + + public Lock(String officeId, String projectId, String applicationId, String acquireTime, + String sessionUser, String osUser, String sessionProgram, + String sessionMachine) { + this.officeId = officeId; + this.projectId = projectId; + this.applicationId = applicationId; + this.acquireTime = acquireTime; + this.sessionUser = sessionUser; + this.osUser = osUser; + this.sessionProgram = sessionProgram; + this.sessionMachine = sessionMachine; + } + + public String getOfficeId() { + return officeId; + } + + public String getProjectId() { + return projectId; + } + + public String getApplicationId() { + return applicationId; + } + + public String getAcquireTime() { + return acquireTime; + } + + public String getSessionUser() { + return sessionUser; + } + + public String getOsUser() { + return osUser; + } + + public String getSessionProgram() { + return sessionProgram; + } + + public String getSessionMachine() { + return sessionMachine; + } + } + + + public void updateLockRevokerRights(LockRevokerRights lock, boolean allow) { + CWMS_PROJECT_PACKAGE.call_UPDATE_LOCK_REVOKER_RIGHTS(dsl.configuration(), + lock.getUserId(), lock.getProjectId(), OracleTypeMap.formatBool(allow), + lock.getApplicationId(), lock.getOfficeId()); + } + + + public List catLockRevokerRights(String projectMask, + String applicationMask, String officeMask) { + return connectionResult(dsl, c -> { + List retval = new ArrayList<>(); + try (ResultSet rs = CWMS_PROJECT_PACKAGE.call_CAT_LOCK_REVOKER_RIGHTS( + DSL.using(c, SQLDialect.ORACLE18C).configuration(), + projectMask, applicationMask, officeMask).intoResultSet()) { + while (rs.next()) { + String officeId = rs.getString("office_id"); + String projectId = rs.getString("project_id"); + String applicationId = rs.getString("application_id"); + String userId = rs.getString("user_id"); + + LockRevokerRights lock = new LockRevokerRights(officeId, projectId, + applicationId, userId); + retval.add(lock); + } + + } + return retval; + }); + } + + + public static class LockRevokerRights { + private final String officeId; + private final String projectId; + private final String applicationId; + private final String userId; + + public LockRevokerRights(String officeId, String projectId, String applicationId, + String userId) { + this.officeId = officeId; + this.projectId = projectId; + this.applicationId = applicationId; + this.userId = userId; + } + + public String getOfficeId() { + return officeId; + } + + public String getProjectId() { + return projectId; + } + + public String getApplicationId() { + return applicationId; + } + + public String getUserId() { + return userId; + } + } + + } From 78bb4bf9c9fdb0c1c33096a9fca3635ccae41f46 Mon Sep 17 00:00:00 2001 From: rma-rripken <89810919+rma-rripken@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:23:58 -0700 Subject: [PATCH 4/9] Moved projects DTO objects into a dto.project package --- .../java/cwms/cda/api/ProjectController.java | 4 +- .../java/cwms/cda/data/dao/ProjectDao.java | 95 +------------------ .../java/cwms/cda/data/dto/project/Lock.java | 57 +++++++++++ .../data/dto/project/LockRevokerRights.java | 32 +++++++ .../cda/data/dto/{ => project}/Project.java | 4 +- .../cda/data/dto/{ => project}/Projects.java | 3 +- .../cwms/cda/api/ProjectControllerIT.java | 2 +- .../java/cwms/cda/data/dto/ProjectTest.java | 1 + .../java/cwms/cda/data/dto/ProjectsTest.java | 2 + 9 files changed, 104 insertions(+), 96 deletions(-) create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/project/Lock.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/project/LockRevokerRights.java rename cwms-data-api/src/main/java/cwms/cda/data/dto/{ => project}/Project.java (98%) rename cwms-data-api/src/main/java/cwms/cda/data/dto/{ => project}/Projects.java (97%) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ProjectController.java b/cwms-data-api/src/main/java/cwms/cda/api/ProjectController.java index 64f170dc0..4883ccf96 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ProjectController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ProjectController.java @@ -28,8 +28,8 @@ import cwms.cda.data.dao.DeleteRule; import cwms.cda.data.dao.JooqDao; import cwms.cda.data.dao.ProjectDao; -import cwms.cda.data.dto.Project; -import cwms.cda.data.dto.Projects; +import cwms.cda.data.dto.project.Project; +import cwms.cda.data.dto.project.Projects; import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; import cwms.cda.formatters.FormattingException; diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java index c63e29b97..4091fb79e 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java @@ -7,8 +7,10 @@ import cwms.cda.api.errors.NotFoundException; import cwms.cda.data.dto.CwmsDTOPaginated; import cwms.cda.data.dto.Location; -import cwms.cda.data.dto.Project; -import cwms.cda.data.dto.Projects; +import cwms.cda.data.dto.project.Lock; +import cwms.cda.data.dto.project.LockRevokerRights; +import cwms.cda.data.dto.project.Project; +import cwms.cda.data.dto.project.Projects; import cwms.cda.helpers.ResourceHelper; import java.math.BigDecimal; import java.math.BigInteger; @@ -677,62 +679,6 @@ public List catLocks(String projMask, String appMask, TimeZone tz, String return retval; } - public static class Lock { - private final String officeId; - private final String projectId; - private final String applicationId; - private final String acquireTime; - private final String sessionUser; - private final String osUser; - private final String sessionProgram; - private final String sessionMachine; - - public Lock(String officeId, String projectId, String applicationId, String acquireTime, - String sessionUser, String osUser, String sessionProgram, - String sessionMachine) { - this.officeId = officeId; - this.projectId = projectId; - this.applicationId = applicationId; - this.acquireTime = acquireTime; - this.sessionUser = sessionUser; - this.osUser = osUser; - this.sessionProgram = sessionProgram; - this.sessionMachine = sessionMachine; - } - - public String getOfficeId() { - return officeId; - } - - public String getProjectId() { - return projectId; - } - - public String getApplicationId() { - return applicationId; - } - - public String getAcquireTime() { - return acquireTime; - } - - public String getSessionUser() { - return sessionUser; - } - - public String getOsUser() { - return osUser; - } - - public String getSessionProgram() { - return sessionProgram; - } - - public String getSessionMachine() { - return sessionMachine; - } - } - public void updateLockRevokerRights(LockRevokerRights lock, boolean allow) { CWMS_PROJECT_PACKAGE.call_UPDATE_LOCK_REVOKER_RIGHTS(dsl.configuration(), @@ -764,37 +710,4 @@ public List catLockRevokerRights(String projectMask, }); } - - public static class LockRevokerRights { - private final String officeId; - private final String projectId; - private final String applicationId; - private final String userId; - - public LockRevokerRights(String officeId, String projectId, String applicationId, - String userId) { - this.officeId = officeId; - this.projectId = projectId; - this.applicationId = applicationId; - this.userId = userId; - } - - public String getOfficeId() { - return officeId; - } - - public String getProjectId() { - return projectId; - } - - public String getApplicationId() { - return applicationId; - } - - public String getUserId() { - return userId; - } - } - - } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Lock.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Lock.java new file mode 100644 index 000000000..991565235 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Lock.java @@ -0,0 +1,57 @@ +package cwms.cda.data.dto.project; + +public class Lock { + private final String officeId; + private final String projectId; + private final String applicationId; + private final String acquireTime; + private final String sessionUser; + private final String osUser; + private final String sessionProgram; + private final String sessionMachine; + + public Lock(String officeId, String projectId, String applicationId, String acquireTime, + String sessionUser, String osUser, String sessionProgram, + String sessionMachine) { + this.officeId = officeId; + this.projectId = projectId; + this.applicationId = applicationId; + this.acquireTime = acquireTime; + this.sessionUser = sessionUser; + this.osUser = osUser; + this.sessionProgram = sessionProgram; + this.sessionMachine = sessionMachine; + } + + public String getOfficeId() { + return officeId; + } + + public String getProjectId() { + return projectId; + } + + public String getApplicationId() { + return applicationId; + } + + public String getAcquireTime() { + return acquireTime; + } + + public String getSessionUser() { + return sessionUser; + } + + public String getOsUser() { + return osUser; + } + + public String getSessionProgram() { + return sessionProgram; + } + + public String getSessionMachine() { + return sessionMachine; + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/project/LockRevokerRights.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/LockRevokerRights.java new file mode 100644 index 000000000..fcbeb96ae --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/LockRevokerRights.java @@ -0,0 +1,32 @@ +package cwms.cda.data.dto.project; + +public class LockRevokerRights { + private final String officeId; + private final String projectId; + private final String applicationId; + private final String userId; + + public LockRevokerRights(String officeId, String projectId, String applicationId, + String userId) { + this.officeId = officeId; + this.projectId = projectId; + this.applicationId = applicationId; + this.userId = userId; + } + + public String getOfficeId() { + return officeId; + } + + public String getProjectId() { + return projectId; + } + + public String getApplicationId() { + return applicationId; + } + + public String getUserId() { + return userId; + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Project.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Project.java similarity index 98% rename from cwms-data-api/src/main/java/cwms/cda/data/dto/Project.java rename to cwms-data-api/src/main/java/cwms/cda/data/dto/project/Project.java index 87be4c425..5e7f1f9fb 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Project.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Project.java @@ -1,4 +1,4 @@ -package cwms.cda.data.dto; +package cwms.cda.data.dto.project; import com.fasterxml.jackson.annotation.JsonInclude; @@ -7,6 +7,8 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import cwms.cda.api.errors.FieldException; +import cwms.cda.data.dto.CwmsDTO; +import cwms.cda.data.dto.Location; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; import cwms.cda.formatters.json.JsonV2; diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Projects.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Projects.java similarity index 97% rename from cwms-data-api/src/main/java/cwms/cda/data/dto/Projects.java rename to cwms-data-api/src/main/java/cwms/cda/data/dto/project/Projects.java index 61a1eef6d..4d4cc8b13 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Projects.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Projects.java @@ -1,6 +1,7 @@ -package cwms.cda.data.dto; +package cwms.cda.data.dto.project; import cwms.cda.api.errors.FieldException; +import cwms.cda.data.dto.CwmsDTOPaginated; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; import cwms.cda.formatters.json.JsonV2; diff --git a/cwms-data-api/src/test/java/cwms/cda/api/ProjectControllerIT.java b/cwms-data-api/src/test/java/cwms/cda/api/ProjectControllerIT.java index 8d8823e20..11e9e243c 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/ProjectControllerIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/ProjectControllerIT.java @@ -32,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import com.fasterxml.jackson.databind.ObjectMapper; -import cwms.cda.data.dto.Project; +import cwms.cda.data.dto.project.Project; import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; import cwms.cda.formatters.json.JsonV2; diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectTest.java index ec90cf572..87125249a 100644 --- a/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectTest.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; +import cwms.cda.data.dto.project.Project; import cwms.cda.formatters.json.JsonV2; import java.io.IOException; import java.io.InputStream; diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectsTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectsTest.java index 98fc775ac..7ef7004d9 100644 --- a/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectsTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectsTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import cwms.cda.data.dto.project.Project; +import cwms.cda.data.dto.project.Projects; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; From 6617b32eb31c8651544c02635481dbaad7c0eedf Mon Sep 17 00:00:00 2001 From: rma-rripken <89810919+rma-rripken@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:15:05 -0700 Subject: [PATCH 5/9] Added a ProjectUtil class. Using existing LocationUtil. Extracted Dao for Lock and LockRevokerRights. --- .../java/cwms/cda/api/ProjectController.java | 2 +- .../data/dao/{ => project}/ProjectDao.java | 297 +----------------- .../cda/data/dao/project/ProjectLockDao.java | 91 ++++++ .../project/ProjectLockRevokerRightsDao.java | 48 +++ .../cda/data/dao/project/ProjectUtil.java | 176 +++++++++++ 5 files changed, 326 insertions(+), 288 deletions(-) rename cwms-data-api/src/main/java/cwms/cda/data/dao/{ => project}/ProjectDao.java (57%) create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectLockDao.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectLockRevokerRightsDao.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectUtil.java diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ProjectController.java b/cwms-data-api/src/main/java/cwms/cda/api/ProjectController.java index 4883ccf96..4cd680ad4 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ProjectController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ProjectController.java @@ -27,7 +27,7 @@ import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.DeleteRule; import cwms.cda.data.dao.JooqDao; -import cwms.cda.data.dao.ProjectDao; +import cwms.cda.data.dao.project.ProjectDao; import cwms.cda.data.dto.project.Project; import cwms.cda.data.dto.project.Projects; import cwms.cda.formatters.ContentType; diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectDao.java similarity index 57% rename from cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java rename to cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectDao.java index 4091fb79e..af9191cf1 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/ProjectDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectDao.java @@ -1,14 +1,16 @@ -package cwms.cda.data.dao; +package cwms.cda.data.dao.project; +import static cwms.cda.data.dao.project.ProjectUtil.getProject; +import static cwms.cda.data.dao.project.ProjectUtil.toProjectT; import static org.jooq.impl.DSL.asterisk; import static org.jooq.impl.DSL.count; import static org.jooq.impl.DSL.noCondition; import cwms.cda.api.errors.NotFoundException; +import cwms.cda.data.dao.DeleteRule; +import cwms.cda.data.dao.JooqDao; import cwms.cda.data.dto.CwmsDTOPaginated; import cwms.cda.data.dto.Location; -import cwms.cda.data.dto.project.Lock; -import cwms.cda.data.dto.project.LockRevokerRights; import cwms.cda.data.dto.project.Project; import cwms.cda.data.dto.project.Projects; import cwms.cda.helpers.ResourceHelper; @@ -23,24 +25,16 @@ import java.util.List; import java.util.TimeZone; import java.util.logging.Logger; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jooq.Condition; import org.jooq.DSLContext; import org.jooq.Record1; -import org.jooq.SQLDialect; import org.jooq.SelectConditionStep; import org.jooq.SelectLimitPercentStep; -import org.jooq.impl.DSL; -import usace.cwms.db.dao.ifc.loc.LocationRefType; -import usace.cwms.db.dao.ifc.loc.LocationType; import usace.cwms.db.dao.util.OracleTypeMap; import usace.cwms.db.jooq.codegen.packages.CWMS_PROJECT_PACKAGE; import usace.cwms.db.jooq.codegen.tables.AV_PROJECT; -import usace.cwms.db.jooq.codegen.udt.records.LOCATION_OBJ_T; -import usace.cwms.db.jooq.codegen.udt.records.LOCATION_REF_T; import usace.cwms.db.jooq.codegen.udt.records.PROJECT_OBJ_T; -import usace.cwms.db.jooq.dao.util.LocationTypeUtil; public class ProjectDao extends JooqDao { private static final Logger logger = Logger.getLogger(ProjectDao.class.getName()); @@ -82,110 +76,7 @@ public Project retrieveProject(String office, String projectId) { getDslContext(c, office).configuration(), projectId, office) ); - return projectObjT == null ? null : buildProject(projectObjT); - } - - private Project buildProject(PROJECT_OBJ_T projectObjT) { - Project.Builder builder = new Project.Builder(); - - LOCATION_OBJ_T projectLocation = projectObjT.getPROJECT_LOCATION(); - LOCATION_REF_T locRef = projectLocation.getLOCATION_REF(); - String office = locRef.getOFFICE_ID(); - builder.withOfficeId(office); - - String id = getLocationId(locRef.getBASE_LOCATION_ID(), locRef.getSUB_LOCATION_ID()); - builder.withName(id); - - String authorizingLaw = projectObjT.getAUTHORIZING_LAW(); - builder.withAuthorizingLaw(authorizingLaw); - - String bankFullCapacityDescription = projectObjT.getBANK_FULL_CAPACITY_DESCRIPTION(); - builder.withBankFullCapacityDesc(bankFullCapacityDescription); - - String downstreamUrbanDescription = projectObjT.getDOWNSTREAM_URBAN_DESCRIPTION(); - builder.withDownstreamUrbanDesc(downstreamUrbanDescription); - - String costUnitsId = projectObjT.getCOST_UNITS_ID(); - builder.withCostUnit(costUnitsId); - - String projectOwner = projectObjT.getPROJECT_OWNER(); - builder.withProjectOwner(projectOwner); - - String hydropowerDescription = projectObjT.getHYDROPOWER_DESCRIPTION(); - builder.withHydropowerDesc(hydropowerDescription); - - String remarks = projectObjT.getREMARKS(); - builder.withProjectRemarks(remarks); - - String sedimentationDescription = projectObjT.getSEDIMENTATION_DESCRIPTION(); - builder.withSedimentationDesc(sedimentationDescription); - - LocationType neargageLocationType = - LocationTypeUtil.toLocationType(projectObjT.getNEAR_GAGE_LOCATION()); - LocationRefType nearLocRef = null; - if (neargageLocationType != null) { - nearLocRef = neargageLocationType.getLocationRef(); - } - if (nearLocRef != null) { - builder = builder.withNearGageLocation(new Location.Builder(nearLocRef.getOfficeId(), - getLocationId(nearLocRef.getBaseLocationId(), - nearLocRef.getSubLocationId())) - .withActive(null) - .build() - ); - } - - LocationType pumpbackLocationType = - LocationTypeUtil.toLocationType(projectObjT.getPUMP_BACK_LOCATION()); - LocationRefType pumpbackLocRef = null; - if (pumpbackLocationType != null) { - pumpbackLocRef = pumpbackLocationType.getLocationRef(); - } - if (pumpbackLocRef != null) { - builder = builder.withPumpBackLocation(new Location.Builder(pumpbackLocRef.getOfficeId(), - getLocationId(pumpbackLocRef.getBaseLocationId(), - pumpbackLocRef.getSubLocationId())) - .withActive(null) - .build() - ); - } - - BigDecimal federalCost = projectObjT.getFEDERAL_COST(); - if (federalCost != null) { - builder = builder.withFederalCost(federalCost.doubleValue()); - } - - BigDecimal federalOandMCost = projectObjT.getFEDERAL_OM_COST(); - if (federalOandMCost != null) { - builder = builder.withFederalOAndMCost(federalOandMCost.doubleValue()); - } - - BigDecimal nonFederalCost = projectObjT.getNONFEDERAL_COST(); - if (nonFederalCost != null) { - builder = builder.withNonFederalCost(nonFederalCost.doubleValue()); - } - - BigDecimal nonFederalOandMCost = projectObjT.getNONFEDERAL_OM_COST(); - if (nonFederalOandMCost != null) { - builder = builder.withNonFederalOAndMCost(nonFederalOandMCost.doubleValue()); - } - - Timestamp costYear = projectObjT.getCOST_YEAR(); - if (costYear != null) { - builder = builder.withCostYear(costYear.toInstant()); - } - - Timestamp yieldTimeFrameEnd = projectObjT.getYIELD_TIME_FRAME_END(); - if (yieldTimeFrameEnd != null) { - builder = builder.withYieldTimeFrameEnd(yieldTimeFrameEnd.toInstant()); - } - - Timestamp yieldTimeFrameStart = projectObjT.getYIELD_TIME_FRAME_START(); - if (yieldTimeFrameStart != null) { - builder = builder.withYieldTimeFrameStart(yieldTimeFrameStart.toInstant()); - } - - return builder.build(); + return projectObjT == null ? null : getProject(projectObjT); } @@ -233,10 +124,7 @@ private static Project buildProject(usace.cwms.db.jooq.codegen.tables.records.AV return builder.build(); } - public static String getLocationId(String base, String sub) { - boolean hasSub = sub != null && !sub.isEmpty(); - return hasSub ? base + "-" + sub : base; - } + public Projects retrieveProjectsFromView(String cursor, int pageSize, String projectIdMask, String office) { @@ -471,31 +359,6 @@ public void create(Project project) { projectT, OracleTypeMap.formatBool(failIfExists))); } - public static LOCATION_REF_T toLocationRefT(String base, String sub, String office) { - return new LOCATION_REF_T(base, sub, office); - } - - - private static @NotNull LOCATION_REF_T getLocationRefT(String locationId, String office) { - String base; - String sub; - if (locationId == null) { - base = null; - sub = null; - } else { - int fieldIndex = locationId.indexOf("-"); - if (fieldIndex == -1) { - base = locationId; - sub = null; - } else { - base = locationId.substring(0, fieldIndex); - sub = locationId.substring(fieldIndex + 1); - } - } - - return toLocationRefT(base, sub, office); - } - public void store(Project project, boolean failIfExists) { String office = project.getOfficeId(); @@ -521,52 +384,7 @@ public void update(Project project) { } - private PROJECT_OBJ_T toProjectT(Project project) { - LOCATION_OBJ_T projectLocation = new LOCATION_OBJ_T(); - projectLocation.setLOCATION_REF(getLocationRefT(project.getName(), project.getOfficeId())); - LOCATION_OBJ_T pumpBackLocation = null; - Location pb = project.getPumpBackLocation(); - if (pb != null) { - pumpBackLocation = new LOCATION_OBJ_T(); - pumpBackLocation.setLOCATION_REF(getLocationRefT(pb.getName(), pb.getOfficeId())); - } - - LOCATION_OBJ_T nearGageLocation = null; - Location ng = project.getNearGageLocation(); - if (ng != null) { - nearGageLocation = new LOCATION_OBJ_T(); - nearGageLocation.setLOCATION_REF(getLocationRefT(ng.getName(), ng.getOfficeId())); - } - - String authorizingLaw = project.getAuthorizingLaw(); - Timestamp costYear = project.getCostYear() != null - ? Timestamp.from(project.getCostYear()) : null; - BigDecimal federalCost = project.getFederalCost() != null - ? BigDecimal.valueOf(project.getFederalCost()) : null; - BigDecimal nonFederalCost = (project.getNonFederalCost() != null) - ? BigDecimal.valueOf(project.getNonFederalCost()) : null; - BigDecimal federalOandMCost = (project.getFederalOAndMCost() != null) - ? BigDecimal.valueOf(project.getFederalOAndMCost()) : null; - BigDecimal nonFederalOandMCost = (project.getNonFederalOAndMCost() != null) - ? BigDecimal.valueOf(project.getNonFederalOAndMCost()) : null; - String costUnitsId = project.getCostUnit(); - String remarks = project.getProjectRemarks(); - String projectOwner = project.getProjectOwner(); - String hydropowerDescription = project.getHydropowerDesc(); - String sedimentationDescription = project.getSedimentationDesc(); - String downstreamUrbanDescription = project.getDownstreamUrbanDesc(); - String bankFullCapacityDescription = project.getBankFullCapacityDesc(); - Timestamp yieldTimeFrameStartTimestamp = (project.getYieldTimeFrameStart() != null) - ? Timestamp.from(project.getYieldTimeFrameStart()) : null; - Timestamp yieldTimeFrameEndTimestamp = (project.getYieldTimeFrameEnd() != null) - ? Timestamp.from(project.getYieldTimeFrameEnd()) : null; - return new PROJECT_OBJ_T(projectLocation, pumpBackLocation, nearGageLocation, - authorizingLaw, costYear, federalCost, nonFederalCost, federalOandMCost, - nonFederalOandMCost, costUnitsId, remarks, projectOwner, hydropowerDescription, - sedimentationDescription, downstreamUrbanDescription, bankFullCapacityDescription, - yieldTimeFrameStartTimestamp, yieldTimeFrameEndTimestamp); - } public void delete(String office, String id, DeleteRule deleteRule) { @@ -590,7 +408,7 @@ public Number publishStatusUpdate(String pProjectId, ); } - protected static BigInteger toBigInteger(Timestamp timestamp) { + public static BigInteger toBigInteger(Timestamp timestamp) { BigInteger retval = null; if (timestamp != null) { retval = BigInteger.valueOf(timestamp.getTime()); @@ -599,7 +417,7 @@ protected static BigInteger toBigInteger(Timestamp timestamp) { return retval; } - protected static BigInteger toBigInteger(Long value) { + public static BigInteger toBigInteger(Long value) { BigInteger retval = null; if (value != null) { retval = BigInteger.valueOf(value); @@ -608,106 +426,11 @@ protected static BigInteger toBigInteger(Long value) { return retval; } - protected static BigInteger toBigInteger(int revokeTimeout) { + public static BigInteger toBigInteger(int revokeTimeout) { return BigInteger.valueOf(revokeTimeout); } - public String requestLock(String projectId, String appId, - boolean revokeExisting, int revokeTimeout, String office) { - BigInteger revokeTimeoutBI = toBigInteger(revokeTimeout); - return connectionResult(dsl, c -> CWMS_PROJECT_PACKAGE.call_REQUEST_LOCK(getDslContext(c, - office).configuration(), - projectId, appId, OracleTypeMap.formatBool(revokeExisting), revokeTimeoutBI, - office)); - } - - - public void releaseLock(String lockId) { - connection(dsl, c -> CWMS_PROJECT_PACKAGE.call_RELEASE_LOCK( - DSL.using(c, SQLDialect.ORACLE18C).configuration(), - lockId)); - } - - - public void revokeLock(String projId, String appId, - int revokeTimeout, String office) { - BigInteger revokeTimeoutBI = toBigInteger(revokeTimeout); - connection(dsl, c -> - CWMS_PROJECT_PACKAGE.call_REVOKE_LOCK(getDslContext(c, office).configuration(), - projId, - appId, revokeTimeoutBI, office)); - } - - public void denyLockRevocation(String lockId) { - connection(dsl, c -> - CWMS_PROJECT_PACKAGE.call_DENY_LOCK_REVOCATION( - DSL.using(c, SQLDialect.ORACLE18C).configuration(), - lockId)); - } - - - public boolean isLocked(String projectId, String appId, String office) { - String s = connectionResult(dsl, c -> CWMS_PROJECT_PACKAGE.call_IS_LOCKED(getDslContext(c - , office).configuration(), - projectId, appId, office)); - return OracleTypeMap.parseBool(s); - } - - - public List catLocks(String projMask, String appMask, TimeZone tz, String officeMask) throws SQLException { - List retval = new ArrayList<>(); - try (ResultSet resultSet = CWMS_PROJECT_PACKAGE.call_CAT_LOCKS(dsl.configuration(), - projMask, appMask, tz.getID(), officeMask) - .intoResultSet()) { - while (resultSet.next()) { - String officeId = resultSet.getString("office_id"); - String projectId = resultSet.getString("project_id"); - String applicationId = resultSet.getString("application_id"); - String acquireTime = resultSet.getString("acquire_time"); - String sessionUser = resultSet.getString("session_user"); - String osUser = resultSet.getString("os_user"); - String sessionProgram = resultSet.getString("session_program"); - String sessionMachine = resultSet.getString("session_machine"); - - Lock lock = new Lock(officeId, projectId, applicationId, acquireTime, sessionUser - , osUser, sessionProgram, sessionMachine); - retval.add(lock); - } - } - return retval; - } - - - public void updateLockRevokerRights(LockRevokerRights lock, boolean allow) { - CWMS_PROJECT_PACKAGE.call_UPDATE_LOCK_REVOKER_RIGHTS(dsl.configuration(), - lock.getUserId(), lock.getProjectId(), OracleTypeMap.formatBool(allow), - lock.getApplicationId(), lock.getOfficeId()); - } - - - public List catLockRevokerRights(String projectMask, - String applicationMask, String officeMask) { - return connectionResult(dsl, c -> { - List retval = new ArrayList<>(); - try (ResultSet rs = CWMS_PROJECT_PACKAGE.call_CAT_LOCK_REVOKER_RIGHTS( - DSL.using(c, SQLDialect.ORACLE18C).configuration(), - projectMask, applicationMask, officeMask).intoResultSet()) { - while (rs.next()) { - String officeId = rs.getString("office_id"); - String projectId = rs.getString("project_id"); - String applicationId = rs.getString("application_id"); - String userId = rs.getString("user_id"); - - LockRevokerRights lock = new LockRevokerRights(officeId, projectId, - applicationId, userId); - retval.add(lock); - } - - } - return retval; - }); - } } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectLockDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectLockDao.java new file mode 100644 index 000000000..1af2a426c --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectLockDao.java @@ -0,0 +1,91 @@ +package cwms.cda.data.dao.project; + +import static cwms.cda.data.dao.project.ProjectDao.toBigInteger; + +import cwms.cda.data.dao.JooqDao; +import cwms.cda.data.dto.project.Lock; +import java.math.BigInteger; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.TimeZone; +import java.util.logging.Logger; +import org.jooq.DSLContext; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; +import usace.cwms.db.dao.util.OracleTypeMap; +import usace.cwms.db.jooq.codegen.packages.CWMS_PROJECT_PACKAGE; + +public class ProjectLockDao extends JooqDao { + private static final Logger logger = Logger.getLogger(ProjectLockDao.class.getName()); + + protected ProjectLockDao(DSLContext dsl) { + super(dsl); + } + + public String requestLock(String projectId, String appId, + boolean revokeExisting, int revokeTimeout, String office) { + BigInteger revokeTimeoutBI = toBigInteger(revokeTimeout); + return connectionResult(dsl, + c -> CWMS_PROJECT_PACKAGE.call_REQUEST_LOCK(getDslContext(c, office).configuration(), + projectId, appId, OracleTypeMap.formatBool(revokeExisting), revokeTimeoutBI, + office)); + } + + public boolean isLocked(String projectId, String appId, String office) { + String s = connectionResult(dsl, + c -> CWMS_PROJECT_PACKAGE.call_IS_LOCKED(getDslContext(c, office).configuration(), + projectId, appId, office)); + return OracleTypeMap.parseBool(s); + } + + + public List catLocks(String projMask, String appMask, TimeZone tz, String officeMask) throws SQLException { + List retval = new ArrayList<>(); + try (ResultSet resultSet = CWMS_PROJECT_PACKAGE.call_CAT_LOCKS(dsl.configuration(), + projMask, appMask, tz.getID(), officeMask) + .intoResultSet()) { + while (resultSet.next()) { + String officeId = resultSet.getString("office_id"); + String projectId = resultSet.getString("project_id"); + String applicationId = resultSet.getString("application_id"); + String acquireTime = resultSet.getString("acquire_time"); + String sessionUser = resultSet.getString("session_user"); + String osUser = resultSet.getString("os_user"); + String sessionProgram = resultSet.getString("session_program"); + String sessionMachine = resultSet.getString("session_machine"); + + Lock lock = new Lock(officeId, projectId, applicationId, acquireTime, sessionUser + , osUser, sessionProgram, sessionMachine); + retval.add(lock); + } + } + return retval; + } + + public void releaseLock(String lockId) { + connection(dsl, c -> CWMS_PROJECT_PACKAGE.call_RELEASE_LOCK( + DSL.using(c, SQLDialect.ORACLE18C).configuration(), + lockId)); + } + + public void revokeLock(String projId, String appId, + int revokeTimeout, String office) { + BigInteger revokeTimeoutBI = toBigInteger(revokeTimeout); + connection(dsl, c -> + CWMS_PROJECT_PACKAGE.call_REVOKE_LOCK(getDslContext(c, office).configuration(), + projId, + appId, revokeTimeoutBI, office)); + } + + + public void denyLockRevocation(String lockId) { + connection(dsl, c -> + CWMS_PROJECT_PACKAGE.call_DENY_LOCK_REVOCATION( + DSL.using(c, SQLDialect.ORACLE18C).configuration(), + lockId)); + } + + +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectLockRevokerRightsDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectLockRevokerRightsDao.java new file mode 100644 index 000000000..c21a92698 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectLockRevokerRightsDao.java @@ -0,0 +1,48 @@ +package cwms.cda.data.dao.project; + +import cwms.cda.data.dao.JooqDao; +import cwms.cda.data.dto.project.LockRevokerRights; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; +import org.jooq.DSLContext; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; +import usace.cwms.db.dao.util.OracleTypeMap; +import usace.cwms.db.jooq.codegen.packages.CWMS_PROJECT_PACKAGE; + +public class ProjectLockRevokerRightsDao extends JooqDao { + protected ProjectLockRevokerRightsDao(DSLContext dsl) { + super(dsl); + } + + public void updateLockRevokerRights(LockRevokerRights lock, boolean allow) { + CWMS_PROJECT_PACKAGE.call_UPDATE_LOCK_REVOKER_RIGHTS(dsl.configuration(), + lock.getUserId(), lock.getProjectId(), OracleTypeMap.formatBool(allow), + lock.getApplicationId(), lock.getOfficeId()); + } + + + public List catLockRevokerRights(String projectMask, + String applicationMask, String officeMask) { + return connectionResult(dsl, c -> { + List retval = new ArrayList<>(); + try (ResultSet rs = CWMS_PROJECT_PACKAGE.call_CAT_LOCK_REVOKER_RIGHTS( + DSL.using(c, SQLDialect.ORACLE18C).configuration(), + projectMask, applicationMask, officeMask).intoResultSet()) { + while (rs.next()) { + String officeId = rs.getString("office_id"); + String projectId = rs.getString("project_id"); + String applicationId = rs.getString("application_id"); + String userId = rs.getString("user_id"); + + LockRevokerRights lock = new LockRevokerRights(officeId, projectId, + applicationId, userId); + retval.add(lock); + } + + } + return retval; + }); + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectUtil.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectUtil.java new file mode 100644 index 000000000..d8e106db1 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectUtil.java @@ -0,0 +1,176 @@ +package cwms.cda.data.dao.project; + +import static cwms.cda.data.dao.location.kind.LocationUtil.getLocationRef; + +import cwms.cda.data.dto.Location; +import cwms.cda.data.dto.project.Project; +import java.math.BigDecimal; +import java.sql.Timestamp; +import usace.cwms.db.dao.ifc.loc.LocationRefType; +import usace.cwms.db.dao.ifc.loc.LocationType; +import usace.cwms.db.jooq.codegen.udt.records.LOCATION_OBJ_T; +import usace.cwms.db.jooq.codegen.udt.records.LOCATION_REF_T; +import usace.cwms.db.jooq.codegen.udt.records.PROJECT_OBJ_T; +import usace.cwms.db.jooq.dao.util.LocationTypeUtil; + +public class ProjectUtil { + private ProjectUtil() { + throw new AssertionError("Utility class"); + } + + public static PROJECT_OBJ_T toProjectT(Project project) { + LOCATION_OBJ_T projectLocation = new LOCATION_OBJ_T(); + projectLocation.setLOCATION_REF(getLocationRef(project.getName(), project.getOfficeId())); + + LOCATION_OBJ_T pumpBackLocation = null; + Location pb = project.getPumpBackLocation(); + if (pb != null) { + pumpBackLocation = new LOCATION_OBJ_T(); + pumpBackLocation.setLOCATION_REF(getLocationRef(pb.getName(), pb.getOfficeId())); + } + + LOCATION_OBJ_T nearGageLocation = null; + Location ng = project.getNearGageLocation(); + if (ng != null) { + nearGageLocation = new LOCATION_OBJ_T(); + nearGageLocation.setLOCATION_REF(getLocationRef(ng.getName(), ng.getOfficeId())); + } + + String authorizingLaw = project.getAuthorizingLaw(); + Timestamp costYear = project.getCostYear() != null + ? Timestamp.from(project.getCostYear()) : null; + BigDecimal federalCost = project.getFederalCost() != null + ? BigDecimal.valueOf(project.getFederalCost()) : null; + BigDecimal nonFederalCost = (project.getNonFederalCost() != null) + ? BigDecimal.valueOf(project.getNonFederalCost()) : null; + BigDecimal federalOandMCost = (project.getFederalOAndMCost() != null) + ? BigDecimal.valueOf(project.getFederalOAndMCost()) : null; + BigDecimal nonFederalOandMCost = (project.getNonFederalOAndMCost() != null) + ? BigDecimal.valueOf(project.getNonFederalOAndMCost()) : null; + String costUnitsId = project.getCostUnit(); + String remarks = project.getProjectRemarks(); + String projectOwner = project.getProjectOwner(); + String hydropowerDescription = project.getHydropowerDesc(); + String sedimentationDescription = project.getSedimentationDesc(); + String downstreamUrbanDescription = project.getDownstreamUrbanDesc(); + String bankFullCapacityDescription = project.getBankFullCapacityDesc(); + Timestamp yieldTimeFrameStartTimestamp = (project.getYieldTimeFrameStart() != null) + ? Timestamp.from(project.getYieldTimeFrameStart()) : null; + Timestamp yieldTimeFrameEndTimestamp = (project.getYieldTimeFrameEnd() != null) + ? Timestamp.from(project.getYieldTimeFrameEnd()) : null; + return new PROJECT_OBJ_T(projectLocation, pumpBackLocation, nearGageLocation, + authorizingLaw, costYear, federalCost, nonFederalCost, federalOandMCost, + nonFederalOandMCost, costUnitsId, remarks, projectOwner, hydropowerDescription, + sedimentationDescription, downstreamUrbanDescription, bankFullCapacityDescription, + yieldTimeFrameStartTimestamp, yieldTimeFrameEndTimestamp); + } + + public static Project getProject(PROJECT_OBJ_T projectObjT) { + Project.Builder builder = new Project.Builder(); + + LOCATION_OBJ_T projectLocation = projectObjT.getPROJECT_LOCATION(); + LOCATION_REF_T locRef = projectLocation.getLOCATION_REF(); + String office = locRef.getOFFICE_ID(); + builder.withOfficeId(office); + + String id = getLocationId(locRef.getBASE_LOCATION_ID(), locRef.getSUB_LOCATION_ID()); + builder.withName(id); + + String authorizingLaw = projectObjT.getAUTHORIZING_LAW(); + builder.withAuthorizingLaw(authorizingLaw); + + String bankFullCapacityDescription = projectObjT.getBANK_FULL_CAPACITY_DESCRIPTION(); + builder.withBankFullCapacityDesc(bankFullCapacityDescription); + + String downstreamUrbanDescription = projectObjT.getDOWNSTREAM_URBAN_DESCRIPTION(); + builder.withDownstreamUrbanDesc(downstreamUrbanDescription); + + String costUnitsId = projectObjT.getCOST_UNITS_ID(); + builder.withCostUnit(costUnitsId); + + String projectOwner = projectObjT.getPROJECT_OWNER(); + builder.withProjectOwner(projectOwner); + + String hydropowerDescription = projectObjT.getHYDROPOWER_DESCRIPTION(); + builder.withHydropowerDesc(hydropowerDescription); + + String remarks = projectObjT.getREMARKS(); + builder.withProjectRemarks(remarks); + + String sedimentationDescription = projectObjT.getSEDIMENTATION_DESCRIPTION(); + builder.withSedimentationDesc(sedimentationDescription); + + LocationType neargageLocationType = + LocationTypeUtil.toLocationType(projectObjT.getNEAR_GAGE_LOCATION()); + LocationRefType nearLocRef = null; + if (neargageLocationType != null) { + nearLocRef = neargageLocationType.getLocationRef(); + } + if (nearLocRef != null) { + builder = builder.withNearGageLocation(new Location.Builder(nearLocRef.getOfficeId(), + getLocationId(nearLocRef.getBaseLocationId(), + nearLocRef.getSubLocationId())) + .withActive(null) + .build() + ); + } + + LocationType pumpbackLocationType = + LocationTypeUtil.toLocationType(projectObjT.getPUMP_BACK_LOCATION()); + LocationRefType pumpbackLocRef = null; + if (pumpbackLocationType != null) { + pumpbackLocRef = pumpbackLocationType.getLocationRef(); + } + if (pumpbackLocRef != null) { + builder = builder.withPumpBackLocation(new Location.Builder(pumpbackLocRef.getOfficeId(), + getLocationId(pumpbackLocRef.getBaseLocationId(), + pumpbackLocRef.getSubLocationId())) + .withActive(null) + .build() + ); + } + + BigDecimal federalCost = projectObjT.getFEDERAL_COST(); + if (federalCost != null) { + builder = builder.withFederalCost(federalCost.doubleValue()); + } + + BigDecimal federalOandMCost = projectObjT.getFEDERAL_OM_COST(); + if (federalOandMCost != null) { + builder = builder.withFederalOAndMCost(federalOandMCost.doubleValue()); + } + + BigDecimal nonFederalCost = projectObjT.getNONFEDERAL_COST(); + if (nonFederalCost != null) { + builder = builder.withNonFederalCost(nonFederalCost.doubleValue()); + } + + BigDecimal nonFederalOandMCost = projectObjT.getNONFEDERAL_OM_COST(); + if (nonFederalOandMCost != null) { + builder = builder.withNonFederalOAndMCost(nonFederalOandMCost.doubleValue()); + } + + Timestamp costYear = projectObjT.getCOST_YEAR(); + if (costYear != null) { + builder = builder.withCostYear(costYear.toInstant()); + } + + Timestamp yieldTimeFrameEnd = projectObjT.getYIELD_TIME_FRAME_END(); + if (yieldTimeFrameEnd != null) { + builder = builder.withYieldTimeFrameEnd(yieldTimeFrameEnd.toInstant()); + } + + Timestamp yieldTimeFrameStart = projectObjT.getYIELD_TIME_FRAME_START(); + if (yieldTimeFrameStart != null) { + builder = builder.withYieldTimeFrameStart(yieldTimeFrameStart.toInstant()); + } + + return builder.build(); + } + + public static String getLocationId(String base, String sub) { + boolean hasSub = sub != null && !sub.isEmpty(); + return hasSub ? base + "-" + sub : base; + } + +} From 8f16d188c51016c06253e76500ab0a37aa17ed25 Mon Sep 17 00:00:00 2001 From: rma-rripken <89810919+rma-rripken@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:56:37 -0700 Subject: [PATCH 6/9] Added a project location The sub-query weren't finding the pb loc or ng locations Added a ProjectUtil class. Using existing LocationUtil. Extracted Dao for Lock and LockRevokerRights. --- .../src/main/java/cwms/cda/ApiServlet.java | 5 + .../cwms/cda/data/dao/project/ProjectDao.java | 230 +++++++++--------- .../cda/data/dao/project/ProjectUtil.java | 59 +---- .../cwms/cda/data/dto/project/Project.java | 34 ++- .../cwms/cda/data/dto/project/Projects.java | 6 +- .../cwms/data/sql/project/project_select.sql | 8 +- .../cwms/cda/api/ProjectControllerIT.java | 77 +++--- .../java/cwms/cda/data/dto/ProjectTest.java | 17 +- .../java/cwms/cda/data/dto/ProjectsTest.java | 11 +- .../test/resources/cwms/cda/api/project.json | 13 +- .../resources/cwms/cda/api/project_new.json | 7 +- .../resources/cwms/cda/data/dto/project.json | 13 +- 12 files changed, 237 insertions(+), 243 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java b/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java index 5d15e0fc4..64be92f1e 100644 --- a/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java +++ b/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java @@ -59,6 +59,7 @@ import cwms.cda.api.OfficeController; import cwms.cda.api.ParametersController; import cwms.cda.api.PoolController; +import cwms.cda.api.ProjectController; import cwms.cda.api.PropertyController; import cwms.cda.api.RatingController; import cwms.cda.api.RatingMetadataController; @@ -167,6 +168,7 @@ "/forecast-spec/*", "/forecast-instance/*", "/standard-text-id/*", + "/projects/*", "/properties/*", "/lookup-types/*", "/embankments/*" @@ -463,6 +465,9 @@ protected void configureRoutes() { String forecastFilePath = "/forecast-instance/{" + NAME + "}/file-data"; get(forecastFilePath, new ForecastFileController(metrics)); addCacheControl(forecastFilePath, 1, TimeUnit.DAYS); + + cdaCrudCache("/projects/{" + Controllers.NAME + "}", + new ProjectController(metrics), requiredRoles,5, TimeUnit.MINUTES); cdaCrudCache(format("/properties/{%s}", Controllers.NAME), new PropertyController(metrics), requiredRoles,1, TimeUnit.DAYS); cdaCrudCache(format("/lookup-types/{%s}", Controllers.NAME), diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectDao.java index af9191cf1..590bc78c4 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectDao.java @@ -1,15 +1,14 @@ package cwms.cda.data.dao.project; +import static cwms.cda.data.dao.project.ProjectUtil.getLocationId; import static cwms.cda.data.dao.project.ProjectUtil.getProject; import static cwms.cda.data.dao.project.ProjectUtil.toProjectT; import static org.jooq.impl.DSL.asterisk; import static org.jooq.impl.DSL.count; -import static org.jooq.impl.DSL.noCondition; import cwms.cda.api.errors.NotFoundException; import cwms.cda.data.dao.DeleteRule; import cwms.cda.data.dao.JooqDao; -import cwms.cda.data.dto.CwmsDTOPaginated; import cwms.cda.data.dto.Location; import cwms.cda.data.dto.project.Project; import cwms.cda.data.dto.project.Projects; @@ -20,6 +19,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -28,11 +28,13 @@ import org.jetbrains.annotations.Nullable; import org.jooq.Condition; import org.jooq.DSLContext; +import org.jooq.Record; import org.jooq.Record1; +import org.jooq.Result; import org.jooq.SelectConditionStep; -import org.jooq.SelectLimitPercentStep; import usace.cwms.db.dao.util.OracleTypeMap; import usace.cwms.db.jooq.codegen.packages.CWMS_PROJECT_PACKAGE; +import usace.cwms.db.jooq.codegen.packages.cwms_project.CAT_PROJECT; import usace.cwms.db.jooq.codegen.tables.AV_PROJECT; import usace.cwms.db.jooq.codegen.udt.records.PROJECT_OBJ_T; @@ -59,6 +61,22 @@ public class ProjectDao extends JooqDao { public static final String YIELD_TIME_FRAME_START = "yield_time_frame_start"; public static final String YIELD_TIME_FRAME_END = "yield_time_frame_end"; + // These are the columns from the PROJECT_CAT cursor + public static final String DB_OFFICE_ID = "DB_OFFICE_ID"; + public static final String BASE_LOCATION_ID = "BASE_LOCATION_ID"; + public static final String SUB_LOCATION_ID = "SUB_LOCATION_ID"; + public static final String TIME_ZONE_NAME = "TIME_ZONE_NAME"; + public static final String LATITUDE = "LATITUDE"; + public static final String LONGITUDE = "LONGITUDE"; + public static final String HORIZONTAL_DATUM = "HORIZONTAL_DATUM"; + public static final String ELEVATION = "ELEVATION"; + public static final String ELEV_UNIT_ID = "ELEV_UNIT_ID"; + public static final String VERTICAL_DATUM = "VERTICAL_DATUM"; + public static final String PUBLIC_NAME = "PUBLIC_NAME"; + public static final String LONG_NAME = "LONG_NAME"; + public static final String DESCRIPTION = "DESCRIPTION"; + public static final String ACTIVE_FLAG = "ACTIVE_FLAG"; + private final Calendar UTC_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("UTC")); private static final String SELECT_PART = @@ -80,102 +98,6 @@ public Project retrieveProject(String office, String projectId) { } - - private static Project buildProject(usace.cwms.db.jooq.codegen.tables.records.AV_PROJECT r) { - Project.Builder builder = new Project.Builder(); - builder.withOfficeId(r.getOFFICE_ID()); - builder.withName(r.getPROJECT_ID()); - builder.withPumpBackLocation(new Location.Builder(r.getOFFICE_ID(), r.getPUMP_BACK_LOCATION_ID()) - .withActive(null) - .build() - ); // Can we assume same office? - builder.withNearGageLocation(new Location.Builder(r.getOFFICE_ID(), r.getNEAR_GAGE_LOCATION_ID()) - .withActive(null) - .build() - ); // Can we assume same office? - - builder.withAuthorizingLaw(r.getAUTHORIZING_LAW()); - builder.withProjectRemarks(r.getPROJECT_REMARKS()); - builder.withProjectOwner(r.getPROJECT_OWNER()); - builder.withHydropowerDesc(r.getHYDROPOWER_DESCRIPTION()); - builder.withSedimentationDesc(r.getSEDIMENTATION_DESCRIPTION()); - builder.withDownstreamUrbanDesc(r.getDOWNSTREAM_URBAN_DESCRIPTION()); - builder.withBankFullCapacityDesc(r.getBANK_FULL_CAPACITY_DESCRIPTION()); - BigDecimal federalCost = r.getFEDERAL_COST(); - if (federalCost != null) { - builder.withFederalCost(federalCost.doubleValue()); - } - BigDecimal nonfederalCost = r.getNONFEDERAL_COST(); - if (nonfederalCost != null) { - builder.withNonFederalCost(nonfederalCost.doubleValue()); - } - Timestamp yieldTimeFrameStart = r.getYIELD_TIME_FRAME_START(); - if (yieldTimeFrameStart != null) { - builder.withYieldTimeFrameStart(yieldTimeFrameStart.toInstant()); - } - Timestamp yieldTimeFrameEnd = r.getYIELD_TIME_FRAME_END(); - if (yieldTimeFrameEnd != null) { - builder.withYieldTimeFrameEnd(yieldTimeFrameEnd.toInstant()); - } - - // The view is missing cost-year, fed_om_cat and nonfed_om_cost and the pump office and - // near gage office. - - return builder.build(); - } - - - - public Projects retrieveProjectsFromView(String cursor, int pageSize, String projectIdMask, - String office) { - - Condition whereClause = - JooqDao.caseInsensitiveLikeRegexNullTrue(AV_PROJECT.AV_PROJECT.PROJECT_ID, - projectIdMask); - if (office != null) { - whereClause = whereClause.and(AV_PROJECT.AV_PROJECT.OFFICE_ID.eq(office)); - } - - String cursorOffice = null; - String cursorProjectId = null; - int total = 0; - if (cursor == null || cursor.isEmpty()) { - SelectConditionStep> count = - dsl.select(count(asterisk())) - .from(AV_PROJECT.AV_PROJECT) - .where(whereClause); - total = count.fetchOne().value1(); - } else { - String[] parts = CwmsDTOPaginated.decodeCursor(cursor); - if (parts.length == 4) { - cursorOffice = parts[0]; - cursorProjectId = parts[1]; - pageSize = Integer.parseInt(parts[2]); - total = Integer.parseInt(parts[3]); - } - } - - Condition pagingCondition = noCondition(); - if (cursorOffice != null || cursorProjectId != null) { - Condition inSameOffice = AV_PROJECT.AV_PROJECT.OFFICE_ID.eq(cursorOffice) - .and(AV_PROJECT.AV_PROJECT.PROJECT_ID.gt(cursorProjectId)); - Condition nextOffice = AV_PROJECT.AV_PROJECT.OFFICE_ID.gt(cursorOffice); - pagingCondition = inSameOffice.or(nextOffice); - } - - SelectLimitPercentStep query = - dsl.selectFrom(AV_PROJECT.AV_PROJECT) - .where(whereClause.and(pagingCondition)) - .orderBy(AV_PROJECT.AV_PROJECT.OFFICE_ID, AV_PROJECT.AV_PROJECT.PROJECT_ID) - .limit(pageSize); - - List projs = query.fetch().map(ProjectDao::buildProject); - - Projects.Builder builder = new Projects.Builder(cursor, pageSize, total); - builder.addAll(projs); - return builder.build(); - } - public Projects retrieveProjectsFromTable(String cursor, int pageSize, @Nullable String projectIdMask, @Nullable String office) { @@ -214,8 +136,7 @@ public Projects retrieveProjectsFromTable(String cursor, int pageSize, List projs = connectionResult(dsl, c -> { List projects; try (PreparedStatement ps = c.prepareStatement(query)) { - fillTableQueryParameters(ps, projectIdMask, office, cursorOffice, cursorProjectId - , finalPageSize); + fillTableQueryParameters(ps, projectIdMask, office, cursorOffice, cursorProjectId, finalPageSize); try (ResultSet resultSet = ps.executeQuery()) { projects = new ArrayList<>(); @@ -235,25 +156,39 @@ public Projects retrieveProjectsFromTable(String cursor, int pageSize, private Project buildProjectFromTableRow(ResultSet resultSet) throws SQLException { Project.Builder builder = new Project.Builder(); - builder.withOfficeId(resultSet.getString(OFFICE_ID)); - builder.withName(resultSet.getString(PROJECT_ID)); + String prjOffice = resultSet.getString(OFFICE_ID); + String prjId = resultSet.getString(PROJECT_ID); + Location prjLoc = new Location.Builder(prjOffice, prjId) + .withActive(null) + .build(); + + builder.withLocation(prjLoc); builder.withAuthorizingLaw(resultSet.getString(AUTHORIZING_LAW)); builder.withProjectOwner(resultSet.getString(PROJECT_OWNER)); builder.withHydropowerDesc(resultSet.getString(HYDROPOWER_DESCRIPTION)); builder.withSedimentationDesc(resultSet.getString(SEDIMENTATION_DESCRIPTION)); builder.withDownstreamUrbanDesc(resultSet.getString(DOWNSTREAM_URBAN_DESCRIPTION)); builder.withBankFullCapacityDesc(resultSet.getString(BANK_FULL_CAPACITY_DESCRIPTION)); - builder.withPumpBackLocation( - new Location.Builder(resultSet.getString(PUMP_BACK_OFFICE_ID), - resultSet.getString(PUMP_BACK_LOCATION_ID)) - .build() - ); - builder.withNearGageLocation( - new Location.Builder(resultSet.getString(NEAR_GAGE_OFFICE_ID), - resultSet.getString(NEAR_GAGE_LOCATION_ID)) - .build() - ); + String pbOffice = resultSet.getString(PUMP_BACK_OFFICE_ID); + String pbId = resultSet.getString(PUMP_BACK_LOCATION_ID); + if (pbOffice != null && pbId != null) { + builder.withPumpBackLocation( + new Location.Builder(pbOffice, pbId) + .withActive(null) + .build() + ); + } + + String ngOffice = resultSet.getString(NEAR_GAGE_OFFICE_ID); + String ngId = resultSet.getString(NEAR_GAGE_LOCATION_ID); + if (ngOffice != null && ngId != null) { + builder.withNearGageLocation( + new Location.Builder(ngOffice, ngId) + .withActive(null) + .build() + ); + } builder.withProjectRemarks(resultSet.getString(PROJECT_REMARKS)); @@ -320,7 +255,7 @@ private static String buildTableQuery(@Nullable String projectIdMask, @Nullable String sql = SELECT_PART; if (projectIdMask != null || office != null || cursorOffice != null || cursorProjectId != null) { - sql += "where ("; + sql += " where ("; if (projectIdMask != null && office != null) { sql += "(regexp_like(project.location_id, ?, 'i'))\n" // projectIdMask @@ -351,7 +286,7 @@ private static String buildTableQuery(@Nullable String projectIdMask, @Nullable public void create(Project project) { boolean failIfExists = true; - String office = project.getOfficeId(); + String office = project.getLocation().getOfficeId(); PROJECT_OBJ_T projectT = toProjectT(project); connection(dsl, @@ -361,7 +296,7 @@ public void create(Project project) { public void store(Project project, boolean failIfExists) { - String office = project.getOfficeId(); + String office = project.getLocation().getOfficeId(); PROJECT_OBJ_T projectT = toProjectT(project); connection(dsl, @@ -371,8 +306,8 @@ public void store(Project project, boolean failIfExists) { } public void update(Project project) { - String office = project.getOfficeId(); - Project existingProject = retrieveProject(office, project.getName()); + String office = project.getLocation().getOfficeId(); + Project existingProject = retrieveProject(office, project.getLocation().getName()); if (existingProject == null) { throw new NotFoundException("Could not find project to update."); } @@ -431,6 +366,65 @@ public static BigInteger toBigInteger(int revokeTimeout) { } + public List catProject(String office) { + + return connectionResult(dsl, c -> { + CAT_PROJECT catProject = CWMS_PROJECT_PACKAGE.call_CAT_PROJECT(getDslContext(c, + office).configuration(), office); + + // catProject has two open ResultSets. + // Other places close the basin one we aren't using + // FYI basinRs here is a MockResultSet + // The MockResultSet.close() impl just sets its internal Result variable to null + // So I suspect that with jOOQ we don't actually have to "close" anything here. + ResultSet basinRs = catProject.getP_BASIN_CAT().intoResultSet(); + if (basinRs != null && !basinRs.isClosed()) { + basinRs.close(); + } + + Result projectCatalog = catProject.getP_PROJECT_CAT(); + return projectCatalog.map(this::buildLocation); + }); + } + + private Location buildLocation(Record r) { + + String office = r.get(DB_OFFICE_ID, String.class); + + String base = r.get(BASE_LOCATION_ID, String.class); + String sub = r.get(SUB_LOCATION_ID, String.class); + String name = getLocationId(base, sub); + Location.Builder builder = new Location.Builder(office, name); + + String timeZoneName = r.get(TIME_ZONE_NAME, String.class); + if (timeZoneName != null) { + builder.withTimeZoneName(ZoneId.of(timeZoneName)); + } + Double latitude = r.get(LATITUDE, Double.class); + if (latitude != null) { + builder.withLatitude(latitude); + } + Double longitude = r.get(LONGITUDE, Double.class); + if (longitude != null) { + builder.withLongitude(longitude); + } + String horizontalDatum = r.get(HORIZONTAL_DATUM, String.class); + if (horizontalDatum != null) { + builder.withHorizontalDatum(horizontalDatum); + } + builder.withElevation(r.get(ELEVATION, Double.class)); + builder.withElevationUnits(r.get(ELEV_UNIT_ID, String.class)); + builder.withVerticalDatum(r.get(VERTICAL_DATUM, String.class)); + builder.withPublicName(r.get(PUBLIC_NAME, String.class)); + builder.withLongName(r.get(LONG_NAME, String.class)); + builder.withDescription(r.get(DESCRIPTION, String.class)); + String activeStr = r.get(ACTIVE_FLAG, String.class); + if (activeStr != null) { + builder.withActive(OracleTypeMap.parseBool(activeStr)); + } + + return builder.build(); + } } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectUtil.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectUtil.java index d8e106db1..2f020024c 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectUtil.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/project/ProjectUtil.java @@ -2,6 +2,7 @@ import static cwms.cda.data.dao.location.kind.LocationUtil.getLocationRef; +import cwms.cda.data.dao.location.kind.LocationUtil; import cwms.cda.data.dto.Location; import cwms.cda.data.dto.project.Project; import java.math.BigDecimal; @@ -19,22 +20,10 @@ private ProjectUtil() { } public static PROJECT_OBJ_T toProjectT(Project project) { - LOCATION_OBJ_T projectLocation = new LOCATION_OBJ_T(); - projectLocation.setLOCATION_REF(getLocationRef(project.getName(), project.getOfficeId())); - - LOCATION_OBJ_T pumpBackLocation = null; - Location pb = project.getPumpBackLocation(); - if (pb != null) { - pumpBackLocation = new LOCATION_OBJ_T(); - pumpBackLocation.setLOCATION_REF(getLocationRef(pb.getName(), pb.getOfficeId())); - } - LOCATION_OBJ_T nearGageLocation = null; - Location ng = project.getNearGageLocation(); - if (ng != null) { - nearGageLocation = new LOCATION_OBJ_T(); - nearGageLocation.setLOCATION_REF(getLocationRef(ng.getName(), ng.getOfficeId())); - } + LOCATION_OBJ_T projectLocation = LocationUtil.getLocation(project.getLocation()); + LOCATION_OBJ_T pumpBackLocation = LocationUtil.getLocation(project.getPumpBackLocation()); + LOCATION_OBJ_T nearGageLocation = LocationUtil.getLocation(project.getNearGageLocation()); String authorizingLaw = project.getAuthorizingLaw(); Timestamp costYear = project.getCostYear() != null @@ -69,12 +58,8 @@ public static Project getProject(PROJECT_OBJ_T projectObjT) { Project.Builder builder = new Project.Builder(); LOCATION_OBJ_T projectLocation = projectObjT.getPROJECT_LOCATION(); - LOCATION_REF_T locRef = projectLocation.getLOCATION_REF(); - String office = locRef.getOFFICE_ID(); - builder.withOfficeId(office); - - String id = getLocationId(locRef.getBASE_LOCATION_ID(), locRef.getSUB_LOCATION_ID()); - builder.withName(id); + Location projectLoc = LocationUtil.getLocation(projectLocation); + builder.withLocation(projectLoc); String authorizingLaw = projectObjT.getAUTHORIZING_LAW(); builder.withAuthorizingLaw(authorizingLaw); @@ -100,35 +85,11 @@ public static Project getProject(PROJECT_OBJ_T projectObjT) { String sedimentationDescription = projectObjT.getSEDIMENTATION_DESCRIPTION(); builder.withSedimentationDesc(sedimentationDescription); - LocationType neargageLocationType = - LocationTypeUtil.toLocationType(projectObjT.getNEAR_GAGE_LOCATION()); - LocationRefType nearLocRef = null; - if (neargageLocationType != null) { - nearLocRef = neargageLocationType.getLocationRef(); - } - if (nearLocRef != null) { - builder = builder.withNearGageLocation(new Location.Builder(nearLocRef.getOfficeId(), - getLocationId(nearLocRef.getBaseLocationId(), - nearLocRef.getSubLocationId())) - .withActive(null) - .build() - ); - } + Location ngLoc = LocationUtil.getLocation(projectObjT.getNEAR_GAGE_LOCATION()); + builder = builder.withNearGageLocation(ngLoc); - LocationType pumpbackLocationType = - LocationTypeUtil.toLocationType(projectObjT.getPUMP_BACK_LOCATION()); - LocationRefType pumpbackLocRef = null; - if (pumpbackLocationType != null) { - pumpbackLocRef = pumpbackLocationType.getLocationRef(); - } - if (pumpbackLocRef != null) { - builder = builder.withPumpBackLocation(new Location.Builder(pumpbackLocRef.getOfficeId(), - getLocationId(pumpbackLocRef.getBaseLocationId(), - pumpbackLocRef.getSubLocationId())) - .withActive(null) - .build() - ); - } + Location pbLoc = LocationUtil.getLocation(projectObjT.getPUMP_BACK_LOCATION()); + builder = builder.withPumpBackLocation(pbLoc); BigDecimal federalCost = projectObjT.getFEDERAL_COST(); if (federalCost != null) { diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Project.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Project.java index 5e7f1f9fb..e6cb12545 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Project.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Project.java @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import cwms.cda.api.errors.FieldException; -import cwms.cda.data.dto.CwmsDTO; +import cwms.cda.data.dto.CwmsDTOBase; import cwms.cda.data.dto.Location; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; @@ -18,9 +18,9 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) @FormattableWith(contentType = Formats.JSON, formatter = JsonV2.class) -public class Project extends CwmsDTO { +public class Project implements CwmsDTOBase { - private final String name; + private final Location location; private final Double federalCost; private final Double nonFederalCost; private final Instant costYear; @@ -41,8 +41,8 @@ public class Project extends CwmsDTO { private Project(Project.Builder builder) { - super(builder.officeId); - this.name = builder.name; + + this.location = builder.location; this.federalCost = builder.federalCost; this.nonFederalCost = builder.nonFederalCost; this.costYear = builder.costYear; @@ -67,6 +67,10 @@ public void validate() throws FieldException { } + public Location getLocation() { + return location; + } + public String getAuthorizingLaw() { return authorizingLaw; } @@ -111,10 +115,6 @@ public String getCostUnit() { return costUnit; } - public String getName() { - return name; - } - public String getProjectOwner() { return projectOwner; } @@ -142,8 +142,8 @@ public Instant getYieldTimeFrameStart() { @JsonPOJOBuilder @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) public static class Builder { - private String officeId; - private String name; + private Location location; + private Double federalCost; private Double nonFederalCost; private Instant costYear; @@ -174,8 +174,7 @@ public Project build() { * @return this builder */ public Builder from(Project project) { - return this.withOfficeId(project.getOfficeId()) - .withName(project.getName()) + return this.withLocation(project.getLocation()) .withFederalCost(project.getFederalCost()) .withNonFederalCost(project.getNonFederalCost()) .withCostYear(project.getCostYear()) @@ -195,13 +194,10 @@ public Builder from(Project project) { .withProjectRemarks(project.getProjectRemarks()); } - public Builder withOfficeId(String officeId) { - this.officeId = officeId; - return this; - } - public Builder withName(String projectId) { - this.name = projectId; + + public Builder withLocation(Location location) { + this.location = location; return this; } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Projects.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Projects.java index 4d4cc8b13..d2b1d263f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Projects.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/project/Projects.java @@ -2,6 +2,7 @@ import cwms.cda.api.errors.FieldException; import cwms.cda.data.dto.CwmsDTOPaginated; +import cwms.cda.data.dto.Location; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; import cwms.cda.formatters.json.JsonV2; @@ -91,8 +92,9 @@ public Projects build() { if (this.workingProjects.projects.size() == this.workingProjects.pageSize) { Project last = this.workingProjects.projects.get(this.workingProjects.projects.size() - 1); - String cursor = encodeCursor(CwmsDTOPaginated.delimiter, last.getOfficeId(), - last.getName()); + Location lastLoc = last.getLocation(); + String cursor = encodeCursor(CwmsDTOPaginated.delimiter, lastLoc.getOfficeId(), + lastLoc.getName()); this.workingProjects.nextPage = encodeCursor(cursor, this.workingProjects.pageSize, this.workingProjects.total); } else { diff --git a/cwms-data-api/src/main/resources/cwms/data/sql/project/project_select.sql b/cwms-data-api/src/main/resources/cwms/data/sql/project/project_select.sql index 0dbf5be4b..dad697c9c 100644 --- a/cwms-data-api/src/main/resources/cwms/data/sql/project/project_select.sql +++ b/cwms-data-api/src/main/resources/cwms/data/sql/project/project_select.sql @@ -54,11 +54,9 @@ from ( select o.office_id as office_id, ||pl.sub_location_id as location_id from cwms_20.cwms_office o, cwms_20.at_base_location bl, - cwms_20.at_physical_location pl, - cwms_20.at_project p + cwms_20.at_physical_location pl where bl.db_office_code = o.office_code and pl.base_location_code = bl.base_location_code - and p.project_location_code = pl.location_code ) pumpback on pumpback.location_code = project.pump_back_location_code left outer join ( select pl.location_code, @@ -68,9 +66,7 @@ from ( select o.office_id as office_id, ||pl.sub_location_id as location_id from cwms_20.cwms_office o, cwms_20.at_base_location bl, - cwms_20.at_physical_location pl, - cwms_20.at_project p + cwms_20.at_physical_location pl where bl.db_office_code = o.office_code and pl.base_location_code = bl.base_location_code - and p.project_location_code = pl.location_code ) neargage on neargage.location_code = project.near_gage_location_code diff --git a/cwms-data-api/src/test/java/cwms/cda/api/ProjectControllerIT.java b/cwms-data-api/src/test/java/cwms/cda/api/ProjectControllerIT.java index 11e9e243c..4982d239c 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/ProjectControllerIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/ProjectControllerIT.java @@ -26,12 +26,16 @@ import static cwms.cda.security.KeyAccessManager.AUTH_HEADER; import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import com.fasterxml.jackson.databind.ObjectMapper; +import cwms.cda.data.dto.Location; import cwms.cda.data.dto.project.Project; import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; @@ -84,7 +88,8 @@ void test_get_create_delete() throws IOException { .statusCode(is(HttpServletResponse.SC_CREATED)) ; - String office = project.getOfficeId(); + Location loc = project.getLocation(); + String office = loc.getOfficeId(); // Retrieve the project and assert that it exists given() .log().ifValidationFails(LogDetail.ALL,true) @@ -93,13 +98,13 @@ void test_get_create_delete() throws IOException { .when() .redirects().follow(true) .redirects().max(3) - .get("projects/" + project.getName()) + .get("projects/" + loc.getName()) .then() .log().ifValidationFails(LogDetail.ALL,true) .assertThat() .statusCode(is(HttpServletResponse.SC_OK)) - .body("office-id", equalTo(office)) - .body("name", equalTo(project.getName())) + .body("location.office-id", equalTo(office)) + .body("location.name", equalTo(loc.getName())) ; // Delete a Project @@ -111,7 +116,7 @@ void test_get_create_delete() throws IOException { .when() .redirects().follow(true) .redirects().max(3) - .delete("projects/" + project.getName()) + .delete("projects/" + loc.getName()) .then() .log().ifValidationFails(LogDetail.ALL,true) .assertThat() @@ -126,7 +131,7 @@ void test_get_create_delete() throws IOException { .when() .redirects().follow(true) .redirects().max(3) - .get("projects/" + project.getName()) + .get("projects/" + loc.getName()) .then() .log().ifValidationFails(LogDetail.ALL,true) .assertThat() @@ -143,6 +148,8 @@ void test_update_does_not_exist() throws IOException { Project project = Formats.parseContent(new ContentType(Formats.JSON), json, Project.class); TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + Location loc = project.getLocation(); + //Try to update the project - should fail b/c it does not exist given() .log().ifValidationFails(LogDetail.ALL, true) @@ -153,7 +160,7 @@ void test_update_does_not_exist() throws IOException { .when() .redirects().follow(true) .redirects().max(3) - .patch("/projects/" + project.getName()) + .patch("/projects/" + loc.getName()) .then() .log().ifValidationFails(LogDetail.ALL, true) .assertThat() @@ -212,8 +219,8 @@ void test_get_all() throws IOException { .assertThat() .statusCode(is(HttpServletResponse.SC_CREATED)) ; - - String office = project.getOfficeId(); + Location loc = project.getLocation(); + String office = loc.getOfficeId(); long expectedCostYear = 1717282800000L; @@ -225,7 +232,7 @@ void test_get_all() throws IOException { .log().ifValidationFails(LogDetail.ALL,true) .accept(Formats.JSON) .queryParam(Controllers.OFFICE, office) - .queryParam(Controllers.ID_MASK, "^" + project.getName() + "$") + .queryParam(Controllers.ID_MASK, "^" + loc.getName() + "$") .when() .redirects().follow(true) .redirects().max(3) @@ -234,8 +241,10 @@ void test_get_all() throws IOException { .log().ifValidationFails(LogDetail.ALL,true) .assertThat() .statusCode(is(HttpServletResponse.SC_OK)) - .body("projects[0].office-id", equalTo(office)) - .body("projects[0].name", equalTo(project.getName())) + .body("projects[0].location", notNullValue()) + .body("projects[0].location", not(empty())) + .body("projects[0].location.office-id", equalTo(office)) + .body("projects[0].location.name", equalTo(loc.getName())) .body("projects[0].federal-cost", equalTo(100.0f)) .body("projects[0].non-federal-cost", equalTo(50.0f)) .body("projects[0].federal-o-and-m-cost", equalTo(10.0f)) @@ -250,10 +259,14 @@ void test_get_all() throws IOException { .body("projects[0].yield-time-frame-start", equalTo(expectedStart)) .body("projects[0].yield-time-frame-end", equalTo(expectedEnd)) .body("projects[0].cost-year", equalTo(expectedCostYear)) -// TODO: .body("projects[0].pump-back-location-id", equalTo("Pumpback Location Id")) -// TODO: .body("projects[0].pump-back-office-id", equalTo("SPK")) -// TODO: .body("projects[0].near-gage-location-id", equalTo("Near Gage Location Id")) -// TODO: .body("projects[0].near-gage-office-id", equalTo("SPK")) + .body("projects[0].pump-back-location", notNullValue()) + .body("projects[0].pump-back-location", not(empty())) + .body("projects[0].pump-back-location.name", equalTo("tgcd-PbLoc")) + .body("projects[0].pump-back-location.office-id", equalTo("SPK")) + .body("projects[0].near-gage-location", notNullValue()) + .body("projects[0].near-gage-location", not(empty())) + .body("projects[0].near-gage-location.name", equalTo("tgcd-NgLoc")) + .body("projects[0].near-gage-location.office-id", equalTo("SPK")) // TODO: .body("projects[0].cost-unit", equalTo("$")) ; @@ -267,7 +280,7 @@ void test_get_all() throws IOException { .when() .redirects().follow(true) .redirects().max(3) - .delete("projects/" + project.getName()) + .delete("projects/" + project.getLocation().getName()) .then() .log().ifValidationFails(LogDetail.ALL,true) .assertThat() @@ -294,7 +307,9 @@ void test_get_all_paged() throws IOException { for (int i = 0; i < 15; i++) { Project.Builder builder = new Project.Builder(); builder.from(project) - .withName(String.format("PageTest%2d", i)); + .withLocation(new Location.Builder(project.getLocation()) + .withName(String.format("PageTest%2d", i)) + .build()); Project build = builder.build(); String projJson = om.writeValueAsString(build); @@ -315,7 +330,7 @@ void test_get_all_paged() throws IOException { .statusCode(is(HttpServletResponse.SC_CREATED)); } - String office = project.getOfficeId(); + String office = project.getLocation().getOfficeId(); try { ExtractableResponse extractableResponse = given() .log().ifValidationFails(LogDetail.ALL, true) @@ -331,12 +346,12 @@ void test_get_all_paged() throws IOException { .log().ifValidationFails(LogDetail.ALL, true) .assertThat() .statusCode(is(HttpServletResponse.SC_OK)) - .body("projects[0].office-id", equalTo(office)) - .body("projects[0].name", equalTo("PageTest 0")) - .body("projects[1].name", equalTo("PageTest 1")) - .body("projects[2].name", equalTo("PageTest 2")) - .body("projects[3].name", equalTo("PageTest 3")) - .body("projects[4].name", equalTo("PageTest 4")) + .body("projects[0].location.office-id", equalTo(office)) + .body("projects[0].location.name", equalTo("PageTest 0")) + .body("projects[1].location.name", equalTo("PageTest 1")) + .body("projects[2].location.name", equalTo("PageTest 2")) + .body("projects[3].location.name", equalTo("PageTest 3")) + .body("projects[4].location.name", equalTo("PageTest 4")) .extract(); String next = extractableResponse.path("next-page"); @@ -358,12 +373,12 @@ void test_get_all_paged() throws IOException { .log().ifValidationFails(LogDetail.ALL, true) .assertThat() .statusCode(is(HttpServletResponse.SC_OK)) - .body("projects[0].office-id", equalTo(office)) - .body("projects[0].name", equalTo("PageTest 5")) - .body("projects[1].name", equalTo("PageTest 6")) - .body("projects[2].name", equalTo("PageTest 7")) - .body("projects[3].name", equalTo("PageTest 8")) - .body("projects[4].name", equalTo("PageTest 9")) + .body("projects[0].location.office-id", equalTo(office)) + .body("projects[0].location.name", equalTo("PageTest 5")) + .body("projects[1].location.name", equalTo("PageTest 6")) + .body("projects[2].location.name", equalTo("PageTest 7")) + .body("projects[3].location.name", equalTo("PageTest 8")) + .body("projects[4].location.name", equalTo("PageTest 9")) ; } finally { for (int i = 0; i < 15; i++) { diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectTest.java index 87125249a..3abe89f15 100644 --- a/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectTest.java @@ -12,6 +12,7 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.time.ZoneId; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; @@ -22,15 +23,21 @@ class ProjectTest { void testProject() throws JsonProcessingException { Location pbLoc = new Location.Builder("SPK","Pumpback Location Id") + .withTimeZoneName(ZoneId.of("UTC")) .withActive(null) .build(); Location ngLoc = new Location.Builder("SPK","Near Gage Location Id") + .withTimeZoneName(ZoneId.of("UTC")) + .withActive(null) + .build(); + + Location prjLoc = new Location.Builder("SPK","Project Location Id") + .withTimeZoneName(ZoneId.of("UTC")) .withActive(null) .build(); Project project = new Project.Builder() - .withOfficeId("SPK") - .withName("Project Id") + .withLocation(prjLoc) .withProjectOwner("Project Owner") .withAuthorizingLaw("Authorizing Law") .withFederalCost(100.0) @@ -71,8 +78,10 @@ void testDeserialize() throws IOException { assertNotNull(project); - assertEquals("SPK", project.getOfficeId()); - assertEquals("Project Id", project.getName()); + Location loc = project.getLocation(); + assertNotNull(loc); + assertEquals("SPK", loc.getOfficeId()); + assertEquals("Project Id", loc.getName()); assertEquals("Project Owner", project.getProjectOwner()); assertEquals("Authorizing Law", project.getAuthorizingLaw()); assertEquals(100.0, project.getFederalCost()); diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectsTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectsTest.java index 7ef7004d9..2ff71abbb 100644 --- a/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectsTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/data/dto/ProjectsTest.java @@ -6,6 +6,7 @@ import cwms.cda.data.dto.project.Project; import cwms.cda.data.dto.project.Projects; +import java.time.ZoneId; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; @@ -116,10 +117,12 @@ void testPaging() { } private static Project buildProject(int i) { - Project project = new Project.Builder() - .withOfficeId(OFFICE) - .withName("Test Project" + i) + + return new Project.Builder() + .withLocation(new Location.Builder(OFFICE, "Test Project" + i) + .withTimeZoneName(ZoneId.of("UTC")) + .withActive(null) + .build()) .build(); - return project; } } \ No newline at end of file diff --git a/cwms-data-api/src/test/resources/cwms/cda/api/project.json b/cwms-data-api/src/test/resources/cwms/cda/api/project.json index 2b7221c92..61fdc8451 100644 --- a/cwms-data-api/src/test/resources/cwms/cda/api/project.json +++ b/cwms-data-api/src/test/resources/cwms/cda/api/project.json @@ -1,6 +1,9 @@ { - "office-id": "SPK", - "name" : "test_get_create_delete", + "location" : { + "office-id" : "SPK", + "name" : "tgcd", + "timezone-name": "UTC" + }, "federal-cost" : 100.0, "non-federal-cost" : 50.0, "cost-year" : 1717282800000, @@ -15,11 +18,13 @@ "bank-full-capacity-desc" : "Bank Full Capacity Description", "pump-back-location" : { "office-id" : "SPK", - "name" : "Pumpback Location Id" + "name" : "tgcd-PbLoc", + "timezone-name": "UTC" }, "near-gage-location" : { "office-id" : "SPK", - "name" : "Near Gage Location Id" + "name" : "tgcd-NgLoc", + "timezone-name": "UTC" }, "yield-time-frame-start" : 1717282800000, "yield-time-frame-end" : 1717308000000, diff --git a/cwms-data-api/src/test/resources/cwms/cda/api/project_new.json b/cwms-data-api/src/test/resources/cwms/cda/api/project_new.json index c81fa8a2f..2955751eb 100644 --- a/cwms-data-api/src/test/resources/cwms/cda/api/project_new.json +++ b/cwms-data-api/src/test/resources/cwms/cda/api/project_new.json @@ -1,5 +1,8 @@ { - "office-id": "SPK", - "name" : "test_update_not_exist", + "location": { + "office-id": "SPK", + "name" : "test_update_not_exist", + "timezone-name": "UTC" + }, "project-remarks" : "this shouldn't exist and the client is going to try and update it." } \ No newline at end of file diff --git a/cwms-data-api/src/test/resources/cwms/cda/data/dto/project.json b/cwms-data-api/src/test/resources/cwms/cda/data/dto/project.json index 59670e1f0..e208e75c1 100644 --- a/cwms-data-api/src/test/resources/cwms/cda/data/dto/project.json +++ b/cwms-data-api/src/test/resources/cwms/cda/data/dto/project.json @@ -1,6 +1,9 @@ { - "office-id": "SPK", - "name" : "Project Id", + "location" : { + "office-id" : "SPK", + "name" : "Project Id", + "timezone-name": "UTC" + }, "federal-cost" : 100.0, "non-federal-cost" : 50.0, "cost-year" : 1717199914902, @@ -15,11 +18,13 @@ "bank-full-capacity-desc" : "Bank Full Capacity Description", "pump-back-location" : { "office-id" : "SPK", - "name" : "Pumpback Location Id" + "name" : "Pumpback Location Id", + "timezone-name": "UTC" }, "near-gage-location" : { "office-id" : "SPK", - "name" : "Near Gage Location Id" + "name" : "Near Gage Location Id", + "timezone-name": "UTC" }, "yield-time-frame-start" : 1717199914902, "yield-time-frame-end" : 1717199914902, From 8c12ebed928d2508dc048b9b28d11fe8b5f045e6 Mon Sep 17 00:00:00 2001 From: rma-rripken <89810919+rma-rripken@users.noreply.github.com> Date: Tue, 11 Jun 2024 17:24:05 -0700 Subject: [PATCH 7/9] Consistently using String.format for the controller path construction. --- cwms-data-api/src/main/java/cwms/cda/ApiServlet.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java b/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java index 64be92f1e..ebf0ae98f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java +++ b/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java @@ -406,9 +406,9 @@ protected void configureRoutes() { new ParametersController(metrics), requiredRoles, 60, TimeUnit.MINUTES); cdaCrudCache("/timezones/{zone}", new TimeZoneController(metrics), requiredRoles,60, TimeUnit.MINUTES); - cdaCrudCache("/levels/{" + Controllers.LEVEL_ID + "}", + cdaCrudCache(format("/levels/{%s}", Controllers.LEVEL_ID), new LevelsController(metrics), requiredRoles,5, TimeUnit.MINUTES); - String levelTsPath = "/levels/{" + Controllers.LEVEL_ID + "}/timeseries"; + String levelTsPath = format("/levels/{%s}/timeseries", Controllers.LEVEL_ID); get(levelTsPath, new LevelsAsTimeSeriesController(metrics)); addCacheControl(levelTsPath, 5, TimeUnit.MINUTES); TimeSeriesController tsController = new TimeSeriesController(metrics); @@ -458,15 +458,15 @@ protected void configureRoutes() { new PoolController(metrics), requiredRoles,5, TimeUnit.MINUTES); cdaCrudCache("/specified-levels/{specified-level-id}", new SpecifiedLevelController(metrics), requiredRoles,5, TimeUnit.MINUTES); - cdaCrudCache("/forecast-instance/{" + Controllers.NAME + "}", + cdaCrudCache(format("/forecast-instance/{%s}", Controllers.NAME), new ForecastInstanceController(metrics), requiredRoles,5, TimeUnit.MINUTES); - cdaCrudCache("/forecast-spec/{" + Controllers.NAME + "}", + cdaCrudCache(format("/forecast-spec/{%s}", Controllers.NAME), new ForecastSpecController(metrics), requiredRoles,5, TimeUnit.MINUTES); - String forecastFilePath = "/forecast-instance/{" + NAME + "}/file-data"; + String forecastFilePath = format("/forecast-instance/{%s}/file-data", NAME); get(forecastFilePath, new ForecastFileController(metrics)); addCacheControl(forecastFilePath, 1, TimeUnit.DAYS); - cdaCrudCache("/projects/{" + Controllers.NAME + "}", + cdaCrudCache(format("/projects/{%s}", Controllers.NAME), new ProjectController(metrics), requiredRoles,5, TimeUnit.MINUTES); cdaCrudCache(format("/properties/{%s}", Controllers.NAME), new PropertyController(metrics), requiredRoles,1, TimeUnit.DAYS); From 44bd2c1e055093052d48633cf1bbb8f4d4d95d49 Mon Sep 17 00:00:00 2001 From: rma-rripken <89810919+rma-rripken@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:00:04 -0700 Subject: [PATCH 8/9] Add location-type-like and location-kind-like to Catalog end-point --- .../java/cwms/cda/api/CatalogController.java | 178 ++++++++++-------- .../main/java/cwms/cda/api/Controllers.java | 16 +- .../data/dao/CatalogRequestParameters.java | 29 ++- .../cwms/cda/data/dao/LocationsDaoImpl.java | 66 ++++--- .../cwms/cda/data/dao/TimeSeriesDaoImpl.java | 11 +- .../main/java/cwms/cda/data/dto/Location.java | 4 +- .../cwms/cda/api/CatalogControllerTestIT.java | 81 ++++++++ 7 files changed, 256 insertions(+), 129 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java b/cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java index f54647bb5..53e8c1d2c 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java @@ -11,6 +11,8 @@ import static cwms.cda.api.Controllers.LOCATIONS; import static cwms.cda.api.Controllers.LOCATION_CATEGORY_LIKE; import static cwms.cda.api.Controllers.LOCATION_GROUP_LIKE; +import static cwms.cda.api.Controllers.LOCATION_KIND_LIKE; +import static cwms.cda.api.Controllers.LOCATION_TYPE_LIKE; import static cwms.cda.api.Controllers.OFFICE; import static cwms.cda.api.Controllers.PAGE; import static cwms.cda.api.Controllers.PAGE_SIZE; @@ -45,6 +47,7 @@ import io.javalin.plugin.openapi.annotations.OpenApiContent; import io.javalin.plugin.openapi.annotations.OpenApiParam; import io.javalin.plugin.openapi.annotations.OpenApiResponse; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -97,74 +100,81 @@ public void getAll(Context ctx) { } @OpenApi( - queryParams = { - @OpenApiParam(name = PAGE, - description = "This end point can return a lot of data, this " - + "identifies where in the request you are." - ), - - @OpenApiParam(name = PAGE_SIZE, - type = Integer.class, - description = "How many entries per page returned. Default 500." - ), - @OpenApiParam(name = UNIT_SYSTEM, - type = UnitSystem.class, - description = UnitSystem.DESCRIPTION - ), - @OpenApiParam(name = OFFICE, - description = "3-4 letter office name representing the district you " - + "want to isolate data to." - ), - @OpenApiParam(name = LIKE, - description = "Posix regular expression matching against the id" - ), - @OpenApiParam(name = TIMESERIES_CATEGORY_LIKE, - description = "Posix regular expression matching against the " - + "timeseries category id" - ), - @OpenApiParam(name = TIMESERIES_GROUP_LIKE, - description = "Posix regular expression matching against the " - + "timeseries group id" - ), - @OpenApiParam(name = LOCATION_CATEGORY_LIKE, + queryParams = { + @OpenApiParam(name = PAGE, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are." + ), + + @OpenApiParam(name = PAGE_SIZE, + type = Integer.class, + description = "How many entries per page returned. Default 500." + ), + @OpenApiParam(name = UNIT_SYSTEM, + type = UnitSystem.class, + description = UnitSystem.DESCRIPTION + ), + @OpenApiParam(name = OFFICE, + description = "3-4 letter office name representing the district you " + + "want to isolate data to." + ), + @OpenApiParam(name = LIKE, + description = "Posix regular expression matching against the id" + ), + @OpenApiParam(name = TIMESERIES_CATEGORY_LIKE, + description = "Posix regular expression matching against the " + + "timeseries category id" + ), + @OpenApiParam(name = TIMESERIES_GROUP_LIKE, + description = "Posix regular expression matching against the " + + "timeseries group id" + ), + @OpenApiParam(name = LOCATION_CATEGORY_LIKE, + description = "Posix regular expression matching against the location" + + " category id" + ), + @OpenApiParam(name = LOCATION_GROUP_LIKE, + description = "Posix regular expression matching against the location" + + " group id" + ), + @OpenApiParam(name = BOUNDING_OFFICE_LIKE, description = "Posix regular expression " + + "matching against the location bounding office. " + + "When this field is used items with no bounding office set will not be present in results."), + @OpenApiParam(name = INCLUDE_EXTENTS, type = Boolean.class, + description = "Whether the returned catalog entries should include timeseries " + + "extents. Only valid for TIMESERIES. " + + "Default is " + INCLUDE_EXTENTS_DEFAULT + "."), + @OpenApiParam(name = EXCLUDE_EMPTY, type = Boolean.class, + description = "Specifies " + + "whether Timeseries that have empty extents " + + "should be excluded from the results. For purposes of this parameter " + + "'empty' is defined as VERSION_TIME, EARLIEST_TIME, LATEST_TIME " + + "and LAST_UPDATE all being null. This parameter does not control " + + "whether the extents are returned to the user, only whether matching " + + "timeseries are excluded. Only valid for TIMESERIES. " + + "Default is " + EXCLUDE_EMPTY_DEFAULT + "."), + @OpenApiParam(name = LOCATION_KIND_LIKE, description = "Posix regular expression matching against the location" - + " category id" + + " kind." ), - @OpenApiParam(name = LOCATION_GROUP_LIKE, + @OpenApiParam(name = LOCATION_TYPE_LIKE, description = "Posix regular expression matching against the location" - + " group id" + + " type." ), - @OpenApiParam(name = BOUNDING_OFFICE_LIKE, description = "Posix regular expression " - + "matching against the location bounding office. " - + "When this field is used items with no bounding office set will not be present in results."), - @OpenApiParam(name = Controllers.INCLUDE_EXTENTS, type = Boolean.class, - description = "Whether the returned catalog entries should include timeseries " - + "extents. Only valid for TIMESERIES. " - + "Default is " + INCLUDE_EXTENTS_DEFAULT + "."), - @OpenApiParam(name = Controllers.EXCLUDE_EMPTY, type = Boolean.class, - description = "Specifies " - + "whether Timeseries that have empty extents " - + "should be excluded from the results. For purposes of this parameter " - + "'empty' is defined as VERSION_TIME, EARLIEST_TIME, LATEST_TIME " - + "and LAST_UPDATE all being null. This parameter does not control " - + "whether the extents are returned to the user, only whether matching " - + "timeseries are excluded. Only valid for TIMESERIES. " - + "Default is " + EXCLUDE_EMPTY_DEFAULT + "."), - }, - pathParams = { - @OpenApiParam(name = "dataset", - type = CatalogableEndpoint.class, - description = "A list of what data? E.g. Timeseries, Locations, Ratings, etc") - }, - responses = {@OpenApiResponse(status = STATUS_200, - description = "A list of elements the data set you've selected.", - content = { - @OpenApiContent(from = Catalog.class, type = Formats.JSONV2), - @OpenApiContent(from = Catalog.class, type = Formats.XML) - } - ) - }, - tags = {TAG} + }, + pathParams = { + @OpenApiParam(name = "dataset", + type = CatalogableEndpoint.class, + description = "A list of what data? E.g. Timeseries, Locations, Ratings, etc") + }, + responses = {@OpenApiResponse(status = STATUS_200, + description = "A list of elements the data set you've selected.", + content = { + @OpenApiContent(from = Catalog.class, type = Formats.JSONV2), + @OpenApiContent(from = Catalog.class, type = Formats.XML) + }) + }, + tags = {TAG} ) @Override public void getOne(@NotNull Context ctx, @NotNull String dataSet) { @@ -208,6 +218,12 @@ public void getOne(@NotNull Context ctx, @NotNull String dataSet) { String boundingOfficeLike = queryParamAsClass(ctx, new String[]{BOUNDING_OFFICE_LIKE}, String.class, null, metrics, name(CatalogController.class.getName(), GET_ONE)); + String locationKind = queryParamAsClass(ctx, new String[]{LOCATION_KIND_LIKE}, + String.class, null, metrics, name(CatalogController.class.getName(), GET_ONE)); + + String locationType = queryParamAsClass(ctx, new String[]{LOCATION_TYPE_LIKE}, + String.class, null, metrics, name(CatalogController.class.getName(), GET_ONE)); + String acceptHeader = ctx.header(ACCEPT); ContentType contentType = Formats.parseHeaderAndQueryParm(acceptHeader, null); Catalog cat = null; @@ -229,25 +245,16 @@ public void getOne(@NotNull Context ctx, @NotNull String dataSet) { .withBoundingOfficeLike(boundingOfficeLike) .withIncludeExtents(includeExtents) .withExcludeEmpty(excludeExtents) + .withLocationKind(locationKind) + .withLocationType(locationType) .build(); cat = tsDao.getTimeSeriesCatalog(cursor, pageSize, parameters); } else if (LOCATIONS.equalsIgnoreCase(valDataSet)) { - Set notSupported = new LinkedHashSet<>(); - notSupported.add(TIMESERIES_CATEGORY_LIKE); - notSupported.add(TIMESERIES_GROUP_LIKE); - notSupported.add(EXCLUDE_EMPTY); - notSupported.add(INCLUDE_EXTENTS); - - Map> queryParamMap = ctx.queryParamMap(); - notSupported.retainAll(queryParamMap.keySet()); - - if (!notSupported.isEmpty()) { - throw new IllegalArgumentException("The following parameters are not yet " - + "supported for location: " + notSupported); - } + warnAboutNotSupported(ctx, new String[]{TIMESERIES_CATEGORY_LIKE, + TIMESERIES_GROUP_LIKE, EXCLUDE_EMPTY, INCLUDE_EXTENTS}); CatalogRequestParameters parameters = new CatalogRequestParameters.Builder() .withUnitSystem(unitSystem) @@ -256,6 +263,8 @@ public void getOne(@NotNull Context ctx, @NotNull String dataSet) { .withLocCatLike(locCategoryLike) .withLocGroupLike(locGroupLike) .withBoundingOfficeLike(boundingOfficeLike) + .withLocationKind(locationKind) + .withLocationType(locationType) .build(); LocationsDao dao = new LocationsDaoImpl(dsl); @@ -269,12 +278,25 @@ public void getOne(@NotNull Context ctx, @NotNull String dataSet) { final CdaError re = new CdaError("Cannot create catalog of requested " + "information"); - logger.info(() -> re + "with url:" + ctx.fullUrl()); + logger.info(() -> re + " with url:" + ctx.fullUrl()); ctx.json(re).status(HttpCode.NOT_FOUND); } } } + private static void warnAboutNotSupported(@NotNull Context ctx, String[] warnAbout) { + Set notSupported = new LinkedHashSet<>(); + Collections.addAll(notSupported, warnAbout); + + Map> queryParamMap = ctx.queryParamMap(); + notSupported.retainAll(queryParamMap.keySet()); + + if (!notSupported.isEmpty()) { + throw new IllegalArgumentException("The following parameters are not yet " + + "supported for this method: " + notSupported); + } + } + @OpenApi(tags = {"Catalog"}, ignore = true) @Override public void update(Context ctx, @NotNull String entry) { diff --git a/cwms-data-api/src/main/java/cwms/cda/api/Controllers.java b/cwms-data-api/src/main/java/cwms/cda/api/Controllers.java index 35ef7e64f..c52bcfb2b 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/Controllers.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/Controllers.java @@ -24,6 +24,8 @@ package cwms.cda.api; +import static com.codahale.metrics.MetricRegistry.name; + import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; @@ -41,8 +43,6 @@ import java.time.ZonedDateTime; import org.jetbrains.annotations.Nullable; -import static com.codahale.metrics.MetricRegistry.name; - public final class Controllers { @@ -130,6 +130,8 @@ public final class Controllers { public static final String SOURCE_ENTITY = "source-entity"; public static final String FORECAST_DATE = "forecast-date"; public static final String ISSUE_DATE = "issue-date"; + public static final String LOCATION_KIND_LIKE = "location-kind-like"; + public static final String LOCATION_TYPE_LIKE = "location-type-like"; public static final String GROUP_ID = "group-id"; public static final String REPLACE_ASSIGNED_LOCS = "replace-assigned-locs"; @@ -364,14 +366,10 @@ public static Instant requiredInstant(Context ctx, String param) { return retval; } - static void addDeprecatedContentTypeWarning(Context ctx, ContentType type) - { - if (type.getType().equalsIgnoreCase(Formats.TAB)) - { + static void addDeprecatedContentTypeWarning(Context ctx, ContentType type) { + if (type.getType().equalsIgnoreCase(Formats.TAB)) { ctx.res.addHeader(DEPRECATED_HEADER, DEPRECATED_TAB); - } - else if (type.getType().equalsIgnoreCase(Formats.CSV)) - { + } else if (type.getType().equalsIgnoreCase(Formats.CSV)) { ctx.res.addHeader(DEPRECATED_HEADER, DEPRECATED_CSV); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/CatalogRequestParameters.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/CatalogRequestParameters.java index e81a7bcec..f652d5538 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/CatalogRequestParameters.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/CatalogRequestParameters.java @@ -18,6 +18,8 @@ public class CatalogRequestParameters { private final String boundingOfficeLike; private final boolean includeExtents; private final boolean excludeEmpty; + private final String locationKind; + private final String locationType; private CatalogRequestParameters(Builder builder) { this.office = builder.office; @@ -30,6 +32,8 @@ private CatalogRequestParameters(Builder builder) { this.boundingOfficeLike = builder.boundingOfficeLike; this.includeExtents = builder.includeExtents; this.excludeEmpty = builder.excludeEmpty; + this.locationKind = builder.locationKind; + this.locationType = builder.locationType; } public String getBoundingOfficeLike() { @@ -72,6 +76,15 @@ public boolean isExcludeEmpty() { return excludeEmpty; } + public String getLocationKind() { + return locationKind; + } + + public String getLocationType() { + return locationType; + } + + public static class Builder { String office; String idLike; @@ -83,6 +96,8 @@ public static class Builder { String boundingOfficeLike; boolean includeExtents = false; private boolean excludeEmpty = true; + String locationKind; + String locationType; public Builder() { @@ -138,6 +153,16 @@ public Builder withExcludeEmpty(boolean excludeExtents) { return this; } + public Builder withLocationKind(String locationKind) { + this.locationKind = locationKind; + return this; + } + + public Builder withLocationType(String locationType) { + this.locationType = locationType; + return this; + } + public static Builder from(CatalogRequestParameters params) { // This NEEDS to include every field in the CatalogRequestParameters return new Builder() @@ -151,6 +176,8 @@ public static Builder from(CatalogRequestParameters params) { .withBoundingOfficeLike(params.boundingOfficeLike) .withIncludeExtents(params.includeExtents) .withExcludeEmpty(params.excludeEmpty) + .withLocationKind(params.locationKind) + .withLocationType(params.locationType) ; } @@ -173,7 +200,7 @@ public boolean needs(Table table) { } if (table == AV_LOC.AV_LOC) { - return boundingOfficeLike != null; + return boundingOfficeLike != null || locationKind != null || locationType != null; } if (table == AV_TS_EXTENTS_UTC) { diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationsDaoImpl.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationsDaoImpl.java index 750c92be7..2d6174e85 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationsDaoImpl.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationsDaoImpl.java @@ -180,7 +180,8 @@ private Location buildLocation(Record loc) { .withMapLabel(loc.get(AV_LOC.MAP_LABEL)) .withBoundingOfficeId(loc.get(AV_LOC.BOUNDING_OFFICE_ID)) .withNearestCity(loc.get(AV_LOC.NEAREST_CITY)) - .withNation(Nation.nationForName(loc.get(AV_LOC.NATION_ID))); + .withNation(Nation.nationForName(loc.get(AV_LOC.NATION_ID))) + ; BigDecimal pubLatitude = loc.get(AV_LOC.PUBLISHED_LATITUDE); BigDecimal pubLongitude = loc.get(AV_LOC.PUBLISHED_LONGITUDE); @@ -219,8 +220,8 @@ public void storeLocation(Location location) throws IOException { connection(dsl, c -> { setOffice(c,location); CwmsDbLoc locJooq = CwmsDbServiceLookup.buildCwmsDb(CwmsDbLoc.class, c); - String elevationUnits = location.getElevationUnits() == null ? - Unit.METER.getValue() : location.getElevationUnits(); + String elevationUnits = location.getElevationUnits() == null + ? Unit.METER.getValue() : location.getElevationUnits(); locJooq.store(c, location.getOfficeId(), location.getName(), location.getStateInitial(), location.getCountyName(), location.getTimezoneName(), location.getLocationType(), @@ -248,8 +249,8 @@ public void renameLocation(String oldLocationName, Location renamedLocation) connection(dsl, c -> { setOffice(c,renamedLocation); CwmsDbLoc locJooq = CwmsDbServiceLookup.buildCwmsDb(CwmsDbLoc.class, c); - String elevationUnits = renamedLocation.getElevationUnits() == null ? - Unit.METER.getValue() : renamedLocation.getElevationUnits(); + String elevationUnits = renamedLocation.getElevationUnits() == null + ? Unit.METER.getValue() : renamedLocation.getElevationUnits(); locJooq.rename(c, renamedLocation.getOfficeId(), oldLocationName, renamedLocation.getName(), renamedLocation.getStateInitial(), renamedLocation.getCountyName(), renamedLocation.getTimezoneName(), @@ -378,9 +379,7 @@ private Catalog getLocationCatalog(Catalog.CatalogPage catPage, int pageSize, Ca "A value must be provided for the idLike field. Specify .* if you don't care."); // "condition" needs to be used by the count query and the results query. - Condition condition = buildWhereCondition(params.getUnitSystem(), params.getOffice(), - params.getIdLike(), params.getLocCatLike(), params.getLocGroupLike(), - params.getBoundingOfficeLike()); + Condition condition = buildWhereCondition(params); int total; String cursorLocation; // The location-id of the cursor in the results @@ -453,39 +452,44 @@ private Catalog getLocationCatalog(Catalog.CatalogPage catPage, int pageSize, Ca return new Catalog(cursorLocation, total, pageSize, entries, params); } - private static Condition addCursorConditions(Condition condition, String cursorOffice, String cursorLocation) { - if (cursorOffice != null) { - Condition officeEqualCur = DSL.upper(AV_LOC2.AV_LOC2.DB_OFFICE_ID).eq(cursorOffice.toUpperCase()); - Condition curOfficeLocationIdGreater = DSL.upper(AV_LOC2.AV_LOC2.LOCATION_ID).gt(cursorLocation); - Condition officeGreaterThanCur = DSL.upper(AV_LOC2.AV_LOC2.DB_OFFICE_ID).gt(cursorOffice.toUpperCase()); - condition = condition.and(officeEqualCur).and(curOfficeLocationIdGreater).or(officeGreaterThanCur); - } else { - condition = condition.and(DSL.upper(AV_LOC2.AV_LOC2.LOCATION_ID).gt(cursorLocation)); - } - return condition; - } - - private static Condition buildWhereCondition(String unitSystem, String office, String idLike, - String categoryLike, String groupLike, String boundingOfficeLike) { + private static Condition buildWhereCondition(CatalogRequestParameters params) { + String idLike = params.getIdLike(); Condition condition = caseInsensitiveLikeRegex(AV_LOC2.AV_LOC2.LOCATION_ID, idLike) .and(AV_LOC2.AV_LOC2.LOCATION_CODE.notEqual(DELETED_TS_MARKER)) - .and(AV_LOC2.AV_LOC2.UNIT_SYSTEM.equalIgnoreCase(unitSystem)); + .and(AV_LOC2.AV_LOC2.UNIT_SYSTEM.equalIgnoreCase(params.getUnitSystem())); + String groupLike = params.getLocGroupLike(); + String categoryLike = params.getLocCatLike(); if (categoryLike == null && groupLike == null) { condition = condition.and(AV_LOC2.AV_LOC2.ALIASED_ITEM.isNull()); } + condition = condition.and(caseInsensitiveLikeRegexNullTrue(AV_LOC2.AV_LOC2.LOC_ALIAS_CATEGORY, categoryLike)); + condition = condition.and(caseInsensitiveLikeRegexNullTrue(AV_LOC2.AV_LOC2.LOC_ALIAS_GROUP, groupLike)); + + String office = params.getOffice(); if (office != null) { condition = condition.and(DSL.upper(AV_LOC2.AV_LOC2.DB_OFFICE_ID).eq(office.toUpperCase())); } - if (categoryLike != null) { - condition = condition.and(caseInsensitiveLikeRegex(AV_LOC2.AV_LOC2.LOC_ALIAS_CATEGORY, categoryLike)); - } - if (groupLike != null) { - condition = condition.and(caseInsensitiveLikeRegex(AV_LOC2.AV_LOC2.LOC_ALIAS_GROUP, groupLike)); - } - if (boundingOfficeLike != null) { - condition = condition.and(caseInsensitiveLikeRegex(AV_LOC2.AV_LOC2.BOUNDING_OFFICE_ID, boundingOfficeLike)); + + condition = condition.and(caseInsensitiveLikeRegexNullTrue(AV_LOC2.AV_LOC2.BOUNDING_OFFICE_ID, + params.getBoundingOfficeLike())); + condition = condition.and(caseInsensitiveLikeRegexNullTrue(AV_LOC2.AV_LOC2.LOCATION_KIND_ID, + params.getLocationKind())); + condition = condition.and(caseInsensitiveLikeRegexNullTrue(AV_LOC2.AV_LOC2.LOCATION_TYPE, + params.getLocationType())); + + return condition; + } + + private static Condition addCursorConditions(Condition condition, String cursorOffice, String cursorLocation) { + if (cursorOffice != null) { + Condition officeEqualCur = DSL.upper(AV_LOC2.AV_LOC2.DB_OFFICE_ID).eq(cursorOffice.toUpperCase()); + Condition curOfficeLocationIdGreater = DSL.upper(AV_LOC2.AV_LOC2.LOCATION_ID).gt(cursorLocation); + Condition officeGreaterThanCur = DSL.upper(AV_LOC2.AV_LOC2.DB_OFFICE_ID).gt(cursorOffice.toUpperCase()); + condition = condition.and(officeEqualCur).and(curOfficeLocationIdGreater).or(officeGreaterThanCur); + } else { + condition = condition.and(DSL.upper(AV_LOC2.AV_LOC2.LOCATION_ID).gt(cursorLocation)); } return condition; } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesDaoImpl.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesDaoImpl.java index 0f3252895..2ad5778e1 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesDaoImpl.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesDaoImpl.java @@ -28,7 +28,6 @@ import cwms.cda.data.dto.VerticalDatumInfo; import cwms.cda.data.dto.catalog.CatalogEntry; import cwms.cda.data.dto.catalog.TimeseriesCatalogEntry; -import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Connection; @@ -52,13 +51,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; - import cwms.cda.formatters.FormattingException; import cwms.cda.formatters.xml.XMLv1; -import cwms.cda.formatters.xml.XMLv2; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jooq.CommonTableExpression; @@ -717,8 +711,9 @@ private Collection buildLocConditions(CatalogRequestParamet if (params.needs(AV_LOC.AV_LOC)) { retval.add(caseInsensitiveLikeRegexNullTrue(AV_LOC.AV_LOC.BOUNDING_OFFICE_ID, params.getBoundingOfficeLike())); - // we could add location_type here too... - // or maybe conditions based on lat/lon + retval.add(caseInsensitiveLikeRegexNullTrue(AV_LOC.AV_LOC.LOCATION_KIND_ID, params.getLocationKind())); + retval.add(caseInsensitiveLikeRegexNullTrue(AV_LOC.AV_LOC.LOCATION_TYPE, params.getLocationType())); + // we could add conditions based on lat/lon here too // or any bool fields. } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java index 64a02dac9..c05de1919 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java @@ -277,8 +277,8 @@ public static class Builder { private final Map> propertyFunctionMap = new HashMap<>(); @JsonCreator - public Builder(@JsonProperty(value = "name") String name, @JsonProperty(value = "location" - + "-kind") String locationKind, + public Builder(@JsonProperty(value = "name") String name, + @JsonProperty(value = "location-kind") String locationKind, @JsonProperty(value = "timezone-name") ZoneId timezoneName, @JsonProperty(value = "latitude") Double latitude, @JsonProperty(value = "longitude") Double longitude, diff --git a/cwms-data-api/src/test/java/cwms/cda/api/CatalogControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/CatalogControllerTestIT.java index f75a664e6..8ba310f37 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/CatalogControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/CatalogControllerTestIT.java @@ -5,12 +5,21 @@ import static cwms.cda.api.Controllers.LIKE; import static cwms.cda.api.Controllers.LOCATION_CATEGORY_LIKE; import static cwms.cda.api.Controllers.LOCATION_GROUP_LIKE; +import static cwms.cda.api.Controllers.LOCATION_KIND_LIKE; import static cwms.cda.api.Controllers.TIMESERIES_CATEGORY_LIKE; import static cwms.cda.api.Controllers.TIMESERIES_GROUP_LIKE; import static org.junit.jupiter.api.Assertions.*; +import cwms.cda.data.dao.DeleteRule; +import cwms.cda.data.dao.project.ProjectDao; +import cwms.cda.data.dto.Location; +import cwms.cda.data.dto.project.Project; +import fixtures.CwmsDataApiSetupCallback; +import java.sql.SQLException; import java.time.Duration; +import java.time.ZoneId; +import org.jooq.DSLContext; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -43,6 +52,9 @@ public static void setup_data() throws Exception { createLocation("Alder Springs",true, OFFICE); createLocation("Wet Meadows",true, OFFICE); createLocation("Pine Flat-Outflow",true, OFFICE); + createLocation("Flat Lake",true, OFFICE); + + createProject("Flat Project", OFFICE); createTimeseries(OFFICE,"Alder Springs.Precip-Cumulative.Inst.15Minutes.0.raw-cda"); createTimeseries(OFFICE,"Alder Springs.Precip-INC.Total.15Minutes.15Minutes.calc-cda"); createTimeseries(OFFICE,"Pine Flat-Outflow.Stage.Inst.15Minutes.0.raw-cda"); @@ -62,9 +74,37 @@ public static void setup_data() throws Exception { } + private static void createProject(String id, String office) throws SQLException { + CwmsDataApiSetupCallback.getDatabaseLink().connection(c -> { + DSLContext dsl = dslContext(c, OFFICE); + ProjectDao projectDao = new ProjectDao(dsl); + Project project = new Project.Builder() + .withLocation(new Location.Builder(id, + "PROJECT", + ZoneId.of("UTC"), + 0.0, + 0.0, + "WGS84", + office) + .build()) + .build(); + projectDao.create(project); + }); + } + + private static void deleteProject(String id, String office) throws SQLException { + CwmsDataApiSetupCallback.getDatabaseLink().connection(c -> { + DSLContext dsl = dslContext(c, OFFICE); + ProjectDao projectDao = new ProjectDao(dsl); + + projectDao.delete(office, id, DeleteRule.DELETE_KEY); + }); + } + @AfterAll public static void deload_data() throws Exception { loadSqlDataFromResource("cwms/cda/data/sql/ts_catalog_cleanup.sql"); + deleteProject("Flat Project", OFFICE); } @Test @@ -280,5 +320,46 @@ void test_ts_with_bounding() { ; } + @Test + void test_loc_kind() { + + String pattern = "^Flat"; + + // First with just the regex. This should match Flat Lake and Flat Project + given() + .accept("application/json;version=2") + .queryParam(Controllers.OFFICE, OFFICE) + .queryParam(LIKE, pattern) + .when() + .get("/catalog/LOCATIONS") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(200)) + .body("$", hasKey("total")) + .body("total", is(2)) + .body("$", hasKey("entries")) + .body("entries.size()", is(2)) + ; + + // Now add the LOCATION_KIND filter + given() + .accept("application/json;version=2") + .queryParam(Controllers.OFFICE, OFFICE) + .queryParam(LIKE, pattern) + .queryParam(LOCATION_KIND_LIKE, "PROJECT") // just Flat Project + .when() + .get("/catalog/LOCATIONS") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(200)) + .body("$", hasKey("total")) + .body("total", is(1)) + .body("$", hasKey("entries")) + .body("entries.size()", is(1)) + .body("entries[0].name", equalTo("Flat Project")) + ; + } } From 95dcc4149484afcfd74bb8d925b53f1e0559faeb Mon Sep 17 00:00:00 2001 From: rma-rripken <89810919+rma-rripken@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:11:09 -0700 Subject: [PATCH 9/9] Minor formatting change --- .../java/cwms/cda/api/CatalogController.java | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java b/cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java index 53e8c1d2c..f3db4bbe3 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java @@ -119,31 +119,33 @@ public void getAll(Context ctx) { + "want to isolate data to." ), @OpenApiParam(name = LIKE, - description = "Posix regular expression matching against the id" + description = "Posix regular expression " + + "matching against the id" ), @OpenApiParam(name = TIMESERIES_CATEGORY_LIKE, - description = "Posix regular expression matching against the " - + "timeseries category id" + description = "Posix regular expression " + + "matching against the timeseries category id" ), @OpenApiParam(name = TIMESERIES_GROUP_LIKE, - description = "Posix regular expression matching against the " - + "timeseries group id" + description = "Posix regular expression " + + "matching against the timeseries group id" ), @OpenApiParam(name = LOCATION_CATEGORY_LIKE, - description = "Posix regular expression matching against the location" - + " category id" + description = "Posix regular expression " + + "matching against the location category id" ), @OpenApiParam(name = LOCATION_GROUP_LIKE, - description = "Posix regular expression matching against the location" - + " group id" + description = "Posix regular expression " + + "matching against the location group id" ), - @OpenApiParam(name = BOUNDING_OFFICE_LIKE, description = "Posix regular expression " - + "matching against the location bounding office. " - + "When this field is used items with no bounding office set will not be present in results."), + @OpenApiParam(name = BOUNDING_OFFICE_LIKE, + description = "Posix regular expression " + + "matching against the location bounding office. When this field is used " + + "items with no bounding office set will not be present in results."), @OpenApiParam(name = INCLUDE_EXTENTS, type = Boolean.class, description = "Whether the returned catalog entries should include timeseries " - + "extents. Only valid for TIMESERIES. " - + "Default is " + INCLUDE_EXTENTS_DEFAULT + "."), + + "extents. Only valid for TIMESERIES. " + + "Default is " + INCLUDE_EXTENTS_DEFAULT + "."), @OpenApiParam(name = EXCLUDE_EMPTY, type = Boolean.class, description = "Specifies " + "whether Timeseries that have empty extents " @@ -154,12 +156,17 @@ public void getAll(Context ctx) { + "timeseries are excluded. Only valid for TIMESERIES. " + "Default is " + EXCLUDE_EMPTY_DEFAULT + "."), @OpenApiParam(name = LOCATION_KIND_LIKE, - description = "Posix regular expression matching against the location" - + " kind." + description = "Posix regular expression matching " + + "against the location kind. The location-kind is typically unset " + + "or one of the following: {\"SITE\", \"EMBANKMENT\", \"OVERFLOW\", " + + "\"TURBINE\", \"STREAM\", \"PROJECT\", \"STREAMGAGE\", \"BASIN\", " + + "\"OUTLET\", \"LOCK\", \"GATE\"}. Multiple kinds can be matched " + + "by using Regular Expression OR clauses. For example: " + + "\"(SITE|STREAM)\"" ), @OpenApiParam(name = LOCATION_TYPE_LIKE, - description = "Posix regular expression matching against the location" - + " type." + description = "Posix regular expression matching " + + "against the location type." ), }, pathParams = {