From c1d525b0e15b9815ed474bb9f89e3b2f78b01fc1 Mon Sep 17 00:00:00 2001 From: Zachary Olson Date: Fri, 26 Jul 2024 08:41:00 -0700 Subject: [PATCH] Implemented controller, store/retrieve limited to one record, still in progress --- .../src/main/java/cwms/cda/ApiServlet.java | 14 +- .../AccountingCatalogController.java | 192 ++++++++++++ .../AccountingCreateController.java | 142 +++++++++ .../WaterSupplyAccountingControllerIT.java | 293 ++++++++++++++++++ .../dto/watersupply/PumpAccountingTest.java | 2 +- .../cwms/cda/api/pump_accounting.json | 43 +++ ...y_accounting.json => pump_accounting.json} | 0 7 files changed, 683 insertions(+), 3 deletions(-) create mode 100644 cwms-data-api/src/main/java/cwms/cda/api/watersupply/AccountingCatalogController.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/api/watersupply/AccountingCreateController.java create mode 100644 cwms-data-api/src/test/java/cwms/cda/api/WaterSupplyAccountingControllerIT.java create mode 100644 cwms-data-api/src/test/resources/cwms/cda/api/pump_accounting.json rename cwms-data-api/src/test/resources/cwms/cda/data/dto/watersupply/{water_supply_accounting.json => pump_accounting.json} (100%) 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 30a8f6a97..a6cbba85a 100644 --- a/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java +++ b/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java @@ -94,6 +94,8 @@ import cwms.cda.api.errors.JsonFieldsException; import cwms.cda.api.errors.NotFoundException; import cwms.cda.api.errors.RequiredQueryParameterException; +import cwms.cda.api.watersupply.AccountingCatalogController; +import cwms.cda.api.watersupply.AccountingCreateController; import cwms.cda.api.watersupply.WaterContractCatalogController; import cwms.cda.api.watersupply.WaterContractController; import cwms.cda.api.watersupply.WaterContractCreateController; @@ -507,8 +509,12 @@ protected void configureRoutes() { addCacheControl(forecastFilePath, 1, TimeUnit.DAYS); addWaterUserHandlers("/projects/{office}/{project-id}/water-user", requiredRoles); - addWaterContractHandlers("/projects/{office}/{project-id}/water-user/{water-user}/contracts", requiredRoles); - addWaterContractTypeHandlers("/projects/{office}/{project-id}/water-user/{water-user}/contracts/{contract-id}/types", requiredRoles); + addWaterContractHandlers("/projects/{office}/{project-id}/water-user/{water-user}/contracts", + requiredRoles); + addAccountingHandlers("/projects/{office}/{project-id}/water-user/{water-user}" + + "/contracts/{contract-id}/accounting", requiredRoles); + addWaterContractTypeHandlers("/projects/{office}/{project-id}/water-user/{water-user}" + + "/contracts/{contract-id}/types", requiredRoles); delete("/projects/{office}/{project-id}/water-user/{water-user}/contracts/{contract-id}/pumps/{name}", new WaterPumpDeleteController(metrics), requiredRoles); @@ -529,6 +535,10 @@ protected void configureRoutes() { new LookupTypeController(metrics), requiredRoles,1, TimeUnit.DAYS); } + private void addAccountingHandlers(String path, RouteRole[] requiredRoles) { + get(path, new AccountingCatalogController(metrics)); + post(path, new AccountingCreateController(metrics), requiredRoles); + } private void addWaterUserHandlers(String path, RouteRole[] requiredRoles) { get(path + "/{water-user}", new WaterUserController(metrics)); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/AccountingCatalogController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/AccountingCatalogController.java new file mode 100644 index 000000000..f4872fa39 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/AccountingCatalogController.java @@ -0,0 +1,192 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.api.watersupply; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import cwms.cda.api.Controllers; +import cwms.cda.api.errors.CdaError; +import cwms.cda.data.dao.watersupply.WaterContractDao; +import cwms.cda.data.dao.watersupply.WaterSupplyAccountingDao; +import cwms.cda.data.dto.CwmsId; +import cwms.cda.data.dto.watersupply.WaterSupplyAccounting; +import cwms.cda.data.dto.watersupply.WaterUser; +import cwms.cda.data.dto.watersupply.WaterUserContract; +import cwms.cda.formatters.ContentType; +import cwms.cda.formatters.Formats; +import cwms.cda.helpers.DateUtils; +import io.javalin.core.util.Header; +import io.javalin.http.Context; +import io.javalin.http.Handler; +import io.javalin.plugin.openapi.annotations.HttpMethod; +import io.javalin.plugin.openapi.annotations.OpenApi; +import io.javalin.plugin.openapi.annotations.OpenApiContent; +import io.javalin.plugin.openapi.annotations.OpenApiParam; +import io.javalin.plugin.openapi.annotations.OpenApiResponse; +import org.jetbrains.annotations.NotNull; +import org.jooq.DSLContext; + +import javax.servlet.http.HttpServletResponse; +import java.time.Instant; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static cwms.cda.api.Controllers.*; +import static cwms.cda.data.dao.JooqDao.getDslContext; + +public class AccountingCatalogController implements Handler { + private final Logger LOGGER = Logger.getLogger(AccountingCatalogController.class.getName()); + private static final String TAG = "Pump Accounting"; + private static final String CONTRACT_ID = "contract-id"; + private static final String START_TIME = "start"; + private static final String START_INCLUSIVE = "start-inclusive"; + private static final String END_INCLUSIVE = "end-inclusive"; + private static final String END_TIME = "end"; + private static final String ROW_LIMIT = "row-limit"; + private static final String ASCENDING = "ascending"; + private final MetricRegistry metrics; + + private Timer.Context markAndTime(String subject) { + return Controllers.markAndTime(metrics, getClass().getName(), subject); + } + + public AccountingCatalogController(MetricRegistry metrics) { + this.metrics = metrics; + } + + @NotNull + protected WaterSupplyAccountingDao getWaterSupplyAccountingDao(DSLContext dsl) { + return new WaterSupplyAccountingDao(dsl); + } + + @OpenApi( + queryParams = { + @OpenApiParam(name = START_TIME, description = "The start time of the time window for " + + "pump accounting entries to retrieve. Defaults to the year 1800."), + @OpenApiParam(name = END_TIME, description = "The end time of the time window for pump " + + "accounting entries to retrieve. Defaults to the year 3000"), + @OpenApiParam(name = START_INCLUSIVE, description = "Whether or not the start time is " + + "inclusive or not. Defaults to TRUE.", type = Boolean.class), + @OpenApiParam(name = END_INCLUSIVE, description = "Whether or not the end time is inclusive " + + "or not. Defaults to TRUE.", type = Boolean.class), + @OpenApiParam(name = ASCENDING, description = "Whether or not the entries should be returned " + + "in ascending order. Defaults to TRUE.", type = Boolean.class), + @OpenApiParam(name = ROW_LIMIT, description = "The maximum number of rows to return. " + + "Defaults to 0, which means no limit.", type = Integer.class) + }, + pathParams = { + @OpenApiParam(name = OFFICE, description = "The office ID the pump accounting is associated with.", + required = true), + @OpenApiParam(name = WATER_USER, description = "The water user the pump accounting is " + + "associated with.", required = true), + @OpenApiParam(name = CONTRACT_ID, description = "The name of the contract associated with " + + "the pump accounting.", required = true), + @OpenApiParam(name = PROJECT_ID, description = "The project ID the pump accounting is " + + "associated with.", required = true) + }, + responses = { + @OpenApiResponse(status = STATUS_200, + content = { + @OpenApiContent(from = WaterSupplyAccountingDao.class, type = Formats.JSONV1), + @OpenApiContent(from = WaterSupplyAccountingDao.class, type = Formats.JSON) + }), + @OpenApiResponse(status = STATUS_404, description = "Pump Accounting not found for " + + "provided input parameters."), + @OpenApiResponse(status = STATUS_501, description = "Requested format is not implemented") + }, + description = "Get pump accounting entries associated with a water supply contract.", + path = "/projects/{office}/water-user/{water-user}/contracts/{contract-id}/accounting", + method = HttpMethod.GET, + tags = {TAG} + ) + + @Override + public void handle(Context ctx) { + try (Timer.Context ignored = markAndTime(GET_ALL)) { + final String office = ctx.pathParam(OFFICE); + final String waterUserName = ctx.pathParam(WATER_USER); + final String contractId = ctx.pathParam(CONTRACT_ID); + final String locationId = ctx.pathParam(PROJECT_ID); + final String startTime = ctx.queryParam(START_TIME) == null + ? "1800-01-01T00:00:00Z" : ctx.queryParam(START_TIME); + final String endTime = ctx.queryParam(END_TIME) == null ? "3000-01-01T00:00:00Z" : ctx.queryParam(END_TIME); + final boolean startInclusive = ctx.queryParam(START_INCLUSIVE) == null + || Boolean.parseBoolean(ctx.queryParam(START_INCLUSIVE)); + final boolean endInclusive = ctx.queryParam(END_INCLUSIVE) == null + || Boolean.parseBoolean(ctx.queryParam(END_INCLUSIVE)); + final boolean ascending = ctx.queryParam(ASCENDING) == null + || Boolean.parseBoolean(ctx.queryParam(ASCENDING)); + final int rowLimit = ctx.queryParam(ROW_LIMIT) != null ? Integer.parseInt(ctx.queryParam(ROW_LIMIT)) : 0; + DSLContext dsl = getDslContext(ctx); + String result; + Instant startInstant = DateUtils.parseUserDate(startTime, "UTC").toInstant(); + Instant endInstant = DateUtils.parseUserDate(endTime, "UTC").toInstant(); + String formatHeader = ctx.header(Header.ACCEPT) != null ? ctx.header(Header.ACCEPT) : Formats.JSONV1; + ContentType contentType = Formats.parseHeader(formatHeader, WaterSupplyAccounting.class); + ctx.contentType(contentType.toString()); + CwmsId projectLocation = new CwmsId.Builder().withOfficeId(office).withName(locationId).build(); + + WaterContractDao contractDao = new WaterContractDao(dsl); + WaterUser waterUser = contractDao.getWaterUser(projectLocation, waterUserName); + List contract = contractDao.getAllWaterContracts(projectLocation, waterUser.getEntityName()); + + if (waterUser.getEntityName() == null) { + CdaError error = new CdaError("Unable to retrieve accounting - no water user found for the" + + " provided parameters."); + LOGGER.log(Level.SEVERE, "Error retrieving water pump accounting - no water user found."); + ctx.status(HttpServletResponse.SC_NOT_FOUND).json(error); + return; + } + + boolean contractExists = false; + for (WaterUserContract contractItem : contract) { + if (contractItem.getContractId().getName().equals(contractId)) { + contractExists = true; + break; + } + } + + if (!contractExists) { + CdaError error = new CdaError("Unable to retrieve accounting - no matching contract found for the" + + " provided parameters."); + LOGGER.log(Level.SEVERE, "Error retrieving water pump accounting - no contract found."); + ctx.status(HttpServletResponse.SC_NOT_FOUND).json(error); + return; + } + + WaterSupplyAccountingDao waterSupplyAccountingDao = getWaterSupplyAccountingDao(dsl); + List accounting = waterSupplyAccountingDao.retrieveAccounting(contractId, waterUser, + projectLocation, null, startInstant, endInstant, startInclusive, endInclusive, + ascending, rowLimit); + + result = Formats.format(contentType, accounting, WaterSupplyAccounting.class); + ctx.result(result); + ctx.status(HttpServletResponse.SC_OK); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/AccountingCreateController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/AccountingCreateController.java new file mode 100644 index 000000000..821889600 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/AccountingCreateController.java @@ -0,0 +1,142 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.api.watersupply; + +import static cwms.cda.api.Controllers.*; +import static cwms.cda.data.dao.JooqDao.getDslContext; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import cwms.cda.api.Controllers; +import cwms.cda.data.dao.LookupTypeDao; +import cwms.cda.data.dao.watersupply.WaterSupplyAccountingDao; +import cwms.cda.data.dto.LookupType; +import cwms.cda.data.dto.watersupply.PumpAccounting; +import cwms.cda.data.dto.watersupply.WaterSupplyAccounting; +import cwms.cda.formatters.ContentType; +import cwms.cda.formatters.Formats; +import io.javalin.core.util.Header; +import io.javalin.http.Context; +import io.javalin.http.Handler; +import io.javalin.plugin.openapi.annotations.HttpMethod; +import io.javalin.plugin.openapi.annotations.OpenApi; +import io.javalin.plugin.openapi.annotations.OpenApiContent; +import io.javalin.plugin.openapi.annotations.OpenApiParam; +import io.javalin.plugin.openapi.annotations.OpenApiRequestBody; +import io.javalin.plugin.openapi.annotations.OpenApiResponse; +import org.jetbrains.annotations.NotNull; +import org.jooq.DSLContext; +import javax.servlet.http.HttpServletResponse; +import java.util.Iterator; +import java.util.List; + + +public class AccountingCreateController implements Handler { + private static final String TAG = "Pump Accounting"; + private static final String CONTRACT_ID = "contract-id"; + private final MetricRegistry metrics; + + private Timer.Context markAndTime(String subject) { + return Controllers.markAndTime(metrics, getClass().getName(), subject); + } + + public AccountingCreateController(MetricRegistry metrics) { + this.metrics = metrics; + } + + @NotNull + protected WaterSupplyAccountingDao getWaterSupplyAccountingDao(DSLContext dsl) { + return new WaterSupplyAccountingDao(dsl); + } + + @OpenApi( + requestBody = @OpenApiRequestBody( + content = { + @OpenApiContent(from = WaterSupplyAccountingDao.class, type = Formats.JSONV1) + }, + required = true), + pathParams = { + @OpenApiParam(name = OFFICE, description = "The office ID the accounting is associated with.", + required = true), + @OpenApiParam(name = WATER_USER, description = "The water user the accounting is associated with.", + required = true), + @OpenApiParam(name = CONTRACT_ID, description = "The name of the contract associated with the accounting.", + required = true), + }, + responses = { + @OpenApiResponse(status = STATUS_204, description = "The pump accounting entry was created."), + @OpenApiResponse(status = STATUS_501, description = "Requested format is not implemented") + }, + description = "Create a new pump accounting entry associated with a water supply contract.", + path = "/projects/{office}/water-user/{water-user}/contracts/{contract-id}/accounting", + method = HttpMethod.POST, + tags = {TAG} + ) + + @Override + public void handle(@NotNull Context ctx) { + try (Timer.Context ignored = markAndTime(CREATE)) { + final String contractId = ctx.pathParam(CONTRACT_ID); + final String office = ctx.pathParam(OFFICE); + DSLContext dsl = getDslContext(ctx); + String formatHeader = ctx.header(Header.ACCEPT) != null ? ctx.header(Header.ACCEPT) : Formats.JSONV1; + ContentType contentType = Formats.parseHeader(formatHeader, WaterSupplyAccounting.class); + ctx.contentType(contentType.toString()); + WaterSupplyAccounting accounting = Formats.parseContent(contentType, ctx.body(), + WaterSupplyAccounting.class); + WaterSupplyAccountingDao waterSupplyAccountingDao = getWaterSupplyAccountingDao(dsl); + LookupTypeDao lookupTypeDao = new LookupTypeDao(dsl); + List lookupList = lookupTypeDao.retrieveLookupTypes("AT_PHYSICAL_TRANSFER_TYPE", "PHYS_TRANS_TYPE", office); + + if (!searchForTransferType(accounting, lookupList)) { + ctx.status(HttpServletResponse.SC_BAD_REQUEST).json("No such transfer type found."); + return; + } + + waterSupplyAccountingDao.storeAccounting(accounting); + ctx.status(HttpServletResponse.SC_CREATED).json(contractId + " created successfully"); + } + } + + private boolean searchForTransferType(WaterSupplyAccounting accounting, List lookupTypes) { + Iterator pumpAccountingIterator = accounting.getPumpAccounting().iterator(); + Iterator lookupTypeIterator = lookupTypes.iterator(); + while (pumpAccountingIterator.hasNext()) { + PumpAccounting pumpAccounting = pumpAccountingIterator.next(); + while (lookupTypeIterator.hasNext()) { + LookupType lookupType = lookupTypeIterator.next(); + if (pumpAccounting.getTransferType().getActive() == lookupType.getActive() + && pumpAccounting.getTransferType().getOfficeId().equals(lookupType.getOfficeId()) + && pumpAccounting.getTransferType().getTooltip().equals(lookupType.getTooltip()) + && pumpAccounting.getTransferType().getDisplayValue().equals(lookupType.getDisplayValue())) { + return true; + } + } + } + return false; + } +} diff --git a/cwms-data-api/src/test/java/cwms/cda/api/WaterSupplyAccountingControllerIT.java b/cwms-data-api/src/test/java/cwms/cda/api/WaterSupplyAccountingControllerIT.java new file mode 100644 index 000000000..8a2e7c680 --- /dev/null +++ b/cwms-data-api/src/test/java/cwms/cda/api/WaterSupplyAccountingControllerIT.java @@ -0,0 +1,293 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.api; + +import cwms.cda.api.enums.Nation; +import cwms.cda.data.dao.DeleteRule; +import cwms.cda.data.dao.LocationsDaoImpl; +import cwms.cda.data.dao.LookupTypeDao; +import cwms.cda.data.dao.project.ProjectDao; +import cwms.cda.data.dao.watersupply.WaterContractDao; +import cwms.cda.data.dto.Location; +import cwms.cda.data.dto.LookupType; +import cwms.cda.data.dto.project.Project; +import cwms.cda.data.dto.watersupply.WaterSupplyAccounting; +import cwms.cda.data.dto.watersupply.WaterUser; +import cwms.cda.data.dto.watersupply.WaterUserContract; +import cwms.cda.formatters.ContentType; +import cwms.cda.formatters.Formats; +import cwms.cda.formatters.json.JsonV1; +import fixtures.CwmsDataApiSetupCallback; +import fixtures.TestAccounts; +import io.restassured.filter.log.LogDetail; +import mil.army.usace.hec.test.database.CwmsDatabaseContainer; +import org.jooq.DSLContext; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.org.apache.commons.io.IOUtils; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.time.ZoneId; + +import static cwms.cda.data.dao.DaoTest.getDslContext; +import static cwms.cda.security.KeyAccessManager.AUTH_HEADER; +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +@Tag("integration") +class WaterSupplyAccountingControllerIT extends DataApiTestIT { + private static final String OFFICE_ID = "SWT"; + private static final WaterSupplyAccounting WATER_SUPPLY_ACCOUNTING; + private static final String START_TIME = "start"; + private static final String START_INCLUSIVE = "start-inclusive"; + private static final String END_INCLUSIVE = "end-inclusive"; + private static final String END_TIME = "end"; + private static final String ROW_LIMIT = "row-limit"; + private static final String ASCENDING = "ascending"; + private static final WaterUserContract CONTRACT; + private static final LookupType testTransferType; + static { + try (InputStream accountStream = WaterSupplyAccounting.class + .getResourceAsStream("/cwms/cda/api/pump_accounting.json"); + InputStream contractStream = WaterUserContract.class + .getResourceAsStream("/cwms/cda/api/waterusercontract.json")) { + assert accountStream != null; + assert contractStream != null; + String contractJson = org.apache.commons.io.IOUtils.toString(contractStream, StandardCharsets.UTF_8); + CONTRACT = Formats.parseContent(new ContentType(Formats.JSONV1), contractJson, WaterUserContract.class); + String accountingJson = IOUtils.toString(accountStream, StandardCharsets.UTF_8); + WATER_SUPPLY_ACCOUNTING = Formats.parseContent(new ContentType(Formats.JSONV1), + accountingJson, WaterSupplyAccounting.class); + testTransferType = WATER_SUPPLY_ACCOUNTING.getPumpAccounting().get(0).getTransferType(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @BeforeAll + static void setup() throws Exception { + // create water contract parent location + // create water user + // create water user contract + + Location contractLocation = new Location.Builder(CONTRACT.getContractId().getOfficeId(), + CONTRACT.getContractId().getName()).withLocationKind("PROJECT") + .withTimeZoneName(ZoneId.of("UTC")) + .withHorizontalDatum("WGS84").withLongitude(78.0).withLatitude(67.9).withVerticalDatum("WGS84") + .withLongName("TEST CONTRACT LOCATION").withActive(true).withMapLabel("LABEL").withNation(Nation.US) + .withElevation(456.7).withElevationUnits("m").withPublishedLongitude(78.9).withPublishedLatitude(45.3) + .withLocationType("PROJECT").withDescription("TEST PROJECT").build(); + Location parentLocation = new Location.Builder(CONTRACT.getWaterUser().getProjectId().getOfficeId(), + CONTRACT.getWaterUser().getProjectId().getName()).withLocationKind("PROJECT") + .withTimeZoneName(ZoneId.of("UTC")).withHorizontalDatum("WGS84") + .withLongitude(38.0).withLatitude(56.5).withVerticalDatum("WGS84") + .withLongName("TEST CONTRACT LOCATION").withActive(true).withMapLabel("LABEL").withNation(Nation.US) + .withElevation(456.7).withElevationUnits("m").withPublishedLongitude(78.9).withPublishedLatitude(45.3) + .withLocationType("PROJECT").withDescription("TEST PROJECT").build(); + + Project project = new Project.Builder().withLocation(parentLocation) + .withFederalCost(BigDecimal.valueOf(123456789)) + .withAuthorizingLaw("NEW LAW").withCostUnit("$") + .withProjectOwner(CONTRACT.getWaterUser().getEntityName()) + .build(); + + WaterUser waterUser = CONTRACT.getWaterUser(); + + CwmsDatabaseContainer databaseLink = CwmsDataApiSetupCallback.getDatabaseLink(); + databaseLink.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + LocationsDaoImpl locationsDao = new LocationsDaoImpl(ctx); + ProjectDao projectDao = new ProjectDao(ctx); + LookupTypeDao lookupTypeDao = new LookupTypeDao(ctx); + WaterContractDao waterContractDao = new WaterContractDao(ctx); + try { + lookupTypeDao.storeLookupType("AT_PHYSICAL_TRANSFER_TYPE","PHYS_TRANS_TYPE", + testTransferType); + locationsDao.storeLocation(contractLocation); + locationsDao.storeLocation(parentLocation); + projectDao.store(project, true); + waterContractDao.storeWaterUser(waterUser, true); + waterContractDao.storeWaterContract(CONTRACT, true, false); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, CwmsDataApiSetupCallback.getWebUser()); + + } + + @AfterAll + static void cleanup() throws Exception { + // delete water user contract + // delete water user + // delete water contract parent location + + Location contractLocation = new Location.Builder(CONTRACT.getContractId().getOfficeId(), + CONTRACT.getContractId().getName()).withLocationKind("PROJECT") + .withTimeZoneName(ZoneId.of("UTC")) + .withHorizontalDatum("WGS84").withLongitude(78.0).withLatitude(67.9).build(); + Location parentLocation = new Location.Builder(CONTRACT.getWaterUser().getProjectId().getOfficeId(), + CONTRACT.getWaterUser().getProjectId().getName()).withLocationKind("PROJECT") + .withTimeZoneName(ZoneId.of("UTC")).withHorizontalDatum("WGS84") + .withLongitude(38.0).withLatitude(56.5).build(); + + CwmsDatabaseContainer databaseLink = CwmsDataApiSetupCallback.getDatabaseLink(); + databaseLink.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + LocationsDaoImpl locationsDao = new LocationsDaoImpl(ctx); + LookupTypeDao lookupTypeDao = new LookupTypeDao(ctx); + ProjectDao projectDao = new ProjectDao(ctx); + lookupTypeDao.deleteLookupType("AT_PHYSICAL_TRANSFER_TYPE", "PHYS_TRANS_TYPE", + OFFICE_ID, testTransferType.getDisplayValue()); + projectDao.delete(CONTRACT.getOfficeId(), CONTRACT.getWaterUser().getProjectId().getName(), + DeleteRule.DELETE_ALL); + locationsDao.deleteLocation(contractLocation.getName(), contractLocation.getOfficeId(), true); + locationsDao.deleteLocation(parentLocation.getName(), parentLocation.getOfficeId(), true); + }, CwmsDataApiSetupCallback.getWebUser()); + } + + @Test + void testCreateRetrieveWaterAccounting() throws Exception { + // Test Structure + // 1) Create pump accounting + // 2) Store pump accounting + // 3) Retrieve pump accounting + // 4) Assert pump accounting is same as created + + TestAccounts.KeyUser user = TestAccounts.KeyUser.SWT_NORMAL; + String json = JsonV1.buildObjectMapper().writeValueAsString(WATER_SUPPLY_ACCOUNTING); + + + // create pump accounting + given() + .log().ifValidationFails(LogDetail.ALL, true) + .contentType(Formats.JSONV1) + .body(json) + .header(AUTH_HEADER, user.toHeaderValue()) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/projects/" + OFFICE_ID + "/" + CONTRACT.getWaterUser().getProjectId().getName() + "/water-user/" + + CONTRACT.getWaterUser().getEntityName() + "/contracts/" + + CONTRACT.getContractId().getName() + "/accounting") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)) + ; + + // retrieve pump accounting + given() + .log().ifValidationFails(LogDetail.ALL, true) + .contentType(Formats.JSONV1) + .header(AUTH_HEADER, user.toHeaderValue()) + .accept(Formats.JSONV1) + .queryParam(START_TIME, "2005-04-05T00:00:00Z") + .queryParam(END_TIME, "2335-04-06T00:00:00Z") + .queryParam(START_INCLUSIVE, "true") + .queryParam(END_INCLUSIVE, "true") + .queryParam(ASCENDING, "true") + .queryParam(ROW_LIMIT, 100) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/projects/" + OFFICE_ID + "/" + CONTRACT.getWaterUser().getProjectId().getName() + "/water-user/" + + CONTRACT.getWaterUser().getEntityName() + "/contracts/" + + CONTRACT.getContractId().getName() + "/accounting") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("[0].contract-name", equalTo(WATER_SUPPLY_ACCOUNTING.getContractName())) + .body("[0].water-user.entity-name", equalTo(WATER_SUPPLY_ACCOUNTING.getWaterUser().getEntityName())) + .body("[0].water-user.project-id.name", equalTo(WATER_SUPPLY_ACCOUNTING.getWaterUser().getProjectId().getName())) + .body("[0].water-user.project-id.office-id", equalTo(WATER_SUPPLY_ACCOUNTING.getWaterUser().getProjectId().getOfficeId())) + .body("[0].water-user.water-right", equalTo(WATER_SUPPLY_ACCOUNTING.getWaterUser().getWaterRight())) + .body("[0].pump-accounting[0].transfer-type.display-value", equalTo(testTransferType.getDisplayValue())) + .body("[0].pump-accounting[0].pump-location.name", equalTo(WATER_SUPPLY_ACCOUNTING.getPumpAccounting().get(0).getPumpLocation().getName())) + .body("[0].pump-accounting[1].pump-location.name", equalTo(WATER_SUPPLY_ACCOUNTING.getPumpAccounting().get(1).getPumpLocation().getName())) + ; + } + + @Test + void testRetrieveNotFoundOutsideTimeWindow() throws Exception { + + // Test Structure + // 1) Store accounting + // 2) Retrieve accounting outside time window + // 3) Assert not found + + TestAccounts.KeyUser user = TestAccounts.KeyUser.SWT_NORMAL; + String json = JsonV1.buildObjectMapper().writeValueAsString(WATER_SUPPLY_ACCOUNTING); + + + // create pump accounting + given() + .log().ifValidationFails(LogDetail.ALL, true) + .contentType(Formats.JSONV1) + .body(json) + .header(AUTH_HEADER, user.toHeaderValue()) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/projects/" + OFFICE_ID + "/" + CONTRACT.getWaterUser().getProjectId().getName() + "/water-user/" + + CONTRACT.getWaterUser().getEntityName() + "/contracts/" + + CONTRACT.getContractId().getName() + "/accounting") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)) + ; + + // retrieve pump accounting + given() + .log().ifValidationFails(LogDetail.ALL, true) + .contentType(Formats.JSONV1) + .header(AUTH_HEADER, user.toHeaderValue()) + .accept(Formats.JSONV1) + .queryParam(START_TIME, "2055-04-05T00:00:00Z") + .queryParam(END_TIME, "2085-04-06T00:00:00Z") + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/projects/" + OFFICE_ID + "/" + CONTRACT.getWaterUser().getProjectId().getName() + "/water-user/" + + CONTRACT.getWaterUser().getEntityName() + "/contracts/" + + CONTRACT.getContractId().getName() + "/accounting") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body(is("[]")) + ; + } +} diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/PumpAccountingTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/PumpAccountingTest.java index e4ac9763b..1c588cdd2 100644 --- a/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/PumpAccountingTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/PumpAccountingTest.java @@ -59,7 +59,7 @@ void testWaterSupplyPumpAccountingSerializationRoundTrip() { void testWaterSupplyPumpAccountingSerializationRoundTripFromFile() throws Exception { WaterSupplyAccounting pumpAccounting = buildTestAccounting(); InputStream resource = this.getClass().getResourceAsStream( - "/cwms/cda/data/dto/watersupply/water_supply_accounting.json"); + "/cwms/cda/data/dto/watersupply/pump_accounting.json"); assertNotNull(resource); String serialized = IOUtils.toString(resource, StandardCharsets.UTF_8); WaterSupplyAccounting deserialized = Formats.parseContent(Formats.parseHeader(Formats.JSONV1, diff --git a/cwms-data-api/src/test/resources/cwms/cda/api/pump_accounting.json b/cwms-data-api/src/test/resources/cwms/cda/api/pump_accounting.json new file mode 100644 index 000000000..ca9c6ec8c --- /dev/null +++ b/cwms-data-api/src/test/resources/cwms/cda/api/pump_accounting.json @@ -0,0 +1,43 @@ +{ + "contract-name": "TEST_CONTRACT", + "water-user": { + "entity-name": "Test User", + "project-id": { + "name": "SACRAMENTO", + "office-id": "SWT" + }, + "water-right": "Test Water Right" + }, + "pump-accounting": [ + { + "pump-location": { + "office-id": "SWT", + "name": "PUMP3" + }, + "transfer-type": { + "office-id": "SWT", + "display-value": "Test Transfer Type", + "tooltip": "Test Tool Tip", + "active": true + }, + "flow": 1.0, + "transfer-date": 10000012648000, + "comment": "Test Comment" + }, + { + "pump-location": { + "office-id": "SWT", + "name": "PUMP1" + }, + "transfer-type": { + "office-id": "SWT", + "display-value": "Test Transfer Type 2", + "tooltip": "Test Tool Tip 2", + "active": true + }, + "flow": 2.0, + "transfer-date": 10000016484000, + "comment": "Test Comment 2" + } + ] +} diff --git a/cwms-data-api/src/test/resources/cwms/cda/data/dto/watersupply/water_supply_accounting.json b/cwms-data-api/src/test/resources/cwms/cda/data/dto/watersupply/pump_accounting.json similarity index 100% rename from cwms-data-api/src/test/resources/cwms/cda/data/dto/watersupply/water_supply_accounting.json rename to cwms-data-api/src/test/resources/cwms/cda/data/dto/watersupply/pump_accounting.json