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 3cf9cf07f..cd1947772 100644 --- a/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java +++ b/cwms-data-api/src/main/java/cwms/cda/ApiServlet.java @@ -71,6 +71,7 @@ import cwms.cda.api.ProjectController; import cwms.cda.api.PropertyController; import cwms.cda.api.RatingController; +import cwms.cda.api.RatingLatestController; import cwms.cda.api.RatingMetadataController; import cwms.cda.api.RatingSpecController; import cwms.cda.api.RatingTemplateController; @@ -504,6 +505,7 @@ protected void configureRoutes() { new RatingSpecController(metrics), requiredRoles,5, TimeUnit.MINUTES); cdaCrudCache("/ratings/metadata/{rating-id}", new RatingMetadataController(metrics), requiredRoles,5, TimeUnit.MINUTES); + get("/ratings/{rating-id}/latest", new RatingLatestController(metrics)); cdaCrudCache("/ratings/{rating-id}", new RatingController(metrics), requiredRoles,5, TimeUnit.MINUTES); cdaCrudCache("/catalog/{dataset}", diff --git a/cwms-data-api/src/main/java/cwms/cda/api/RatingController.java b/cwms-data-api/src/main/java/cwms/cda/api/RatingController.java index edc330ced..8add051e2 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/RatingController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/RatingController.java @@ -296,19 +296,16 @@ public void getAll(@NotNull Context ctx) { ContentType contentType = Formats.parseHeaderAndQueryParm(header, format, RatingAliasMarker.class); - if (format.isEmpty()) - { + if (format.isEmpty()) { //Use the full content type here (i.e. application/json;version=2) ctx.contentType(contentType.toString()); - } - else - { + } else { //Legacy content type only includes the basic type (i.e. application/json) ctx.contentType(contentType.getType()); } - //At the moment, we still use the legacy formatting here, since we don't have a newer API for serializing/deserializing - //a collection of rating sets - unlike getOne. + //At the moment, we still use the legacy formatting here, since we don't have a newer API for + // serializing/deserializing a collection of rating sets - unlike getOne. String legacyFormat = Formats.getLegacyTypeFromContentType(contentType); String results = ratingDao.retrieveRatings(legacyFormat, names, unit, datum, office, start, end, timezone); @@ -322,7 +319,8 @@ public void getAll(@NotNull Context ctx) { @OpenApi( pathParams = { - @OpenApiParam(name = RATING_ID, required = true, description = "The rating-id of the effective dates to be retrieve. "), + @OpenApiParam(name = RATING_ID, required = true, description = "The rating-id of the effective " + + "dates to be retrieve. "), }, queryParams = { @OpenApiParam(name = OFFICE, required = true, description = @@ -354,7 +352,7 @@ public void getAll(@NotNull Context ctx) { public void getOne(@NotNull Context ctx, @NotNull String rating) { try (final Timer.Context ignored = markAndTime(GET_ONE)) { - String officeId = ctx.queryParam(OFFICE); + String timezone = ctx.queryParamAsClass(TIMEZONE, String.class).getOrDefault("UTC"); Instant beginInstant = null; @@ -369,6 +367,8 @@ public void getOne(@NotNull Context ctx, @NotNull String rating) { endInstant = DateUtils.parseUserDate(end, timezone).toInstant(); } + String officeId = ctx.queryParam(OFFICE); + RatingSet.DatabaseLoadMethod method = ctx.queryParamAsClass(METHOD, RatingSet.DatabaseLoadMethod.class) .getOrDefault(RatingSet.DatabaseLoadMethod.EAGER); @@ -381,9 +381,8 @@ public void getOne(@NotNull Context ctx, @NotNull String rating) { } } - @Nullable - private String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod method, + protected String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod method, String officeId, String rating, Instant begin, Instant end) { String retval = null; @@ -398,7 +397,9 @@ private String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod meth if (isJson || isXml) { ctx.contentType(contentType.toString()); try { - RatingSet ratingSet = getRatingSet(ctx, method, officeId, rating, begin, end); + RatingSet ratingSet = null; + ratingSet = getRatingSet(ctx, method, officeId, rating, begin, end); + if (ratingSet != null) { if (isJson) { retval = JsonRatingUtils.toJson(ratingSet); @@ -424,8 +425,7 @@ private String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod meth } else { CdaError re = new CdaError("Currently supporting only: " + Formats.JSONV2 + " and " + Formats.XMLV2); - logger.log(Level.WARNING, "Provided accept header not recognized:" - + acceptHeader, re); + logger.log(Level.WARNING, String.format("Provided accept header not recognized: %s", acceptHeader), re); ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED); ctx.json(CdaError.notImplemented()); } @@ -436,8 +436,8 @@ private String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod meth } private RatingSet getRatingSet(Context ctx, RatingSet.DatabaseLoadMethod method, - String officeId, String rating, Instant begin, - Instant end) throws IOException, RatingException { + String officeId, String rating, Instant begin, + Instant end) throws IOException, RatingException { RatingSet ratingSet; try (final Timer.Context ignored = markAndTime("getRatingSet")) { DSLContext dsl = getDslContext(ctx); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/RatingLatestController.java b/cwms-data-api/src/main/java/cwms/cda/api/RatingLatestController.java new file mode 100644 index 000000000..b5b57c976 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/api/RatingLatestController.java @@ -0,0 +1,133 @@ +/* + * + * 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 static cwms.cda.api.Controllers.GET_ONE; +import static cwms.cda.api.Controllers.METHOD; +import static cwms.cda.api.Controllers.OFFICE; +import static cwms.cda.api.Controllers.RATING_ID; +import static cwms.cda.api.Controllers.STATUS_200; +import static cwms.cda.api.Controllers.TIMEZONE; +import static cwms.cda.data.dao.JooqDao.getDslContext; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import cwms.cda.data.dao.JsonRatingUtils; +import cwms.cda.data.dao.RatingDao; +import cwms.cda.data.dao.RatingSetDao; +import cwms.cda.formatters.Formats; + +import hec.data.RatingException; +import hec.data.cwmsRating.RatingSet; +import io.javalin.http.Context; +import io.javalin.http.Handler; +import io.javalin.http.HttpCode; +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 java.io.IOException; +import org.jetbrains.annotations.NotNull; +import org.jooq.DSLContext; + + +public class RatingLatestController implements Handler { + private static final String TAG = "Ratings"; + private final MetricRegistry metrics; + + public RatingLatestController(MetricRegistry metrics) { + this.metrics = metrics; + } + + private Timer.Context markAndTime(String subject) { + return Controllers.markAndTime(metrics, getClass().getName(), subject); + } + + @NotNull + protected RatingDao getRatingDao(DSLContext dsl) { + return new RatingSetDao(dsl); + } + + @OpenApi( + pathParams = { + @OpenApiParam(name = RATING_ID, required = true, description = "The rating-id of the effective " + + "dates to be retrieve. "), + }, + queryParams = { + @OpenApiParam(name = OFFICE, required = true, description = + "Specifies the owning office of the ratingset to be included in the " + + "response."), + @OpenApiParam(name = TIMEZONE, description = "Specifies " + + "the time zone of the values of the begin and end fields (unless " + + "otherwise specified), as well as the time zone of any times in the" + + " response. If this field is not specified, the default time zone " + + "of UTC shall be used."), + @OpenApiParam(name = METHOD, description = "Specifies " + + "the retrieval method used. If no method is provided EAGER will be used.", + type = RatingSet.DatabaseLoadMethod.class), + }, + responses = { + @OpenApiResponse(status = STATUS_200, content = { + @OpenApiContent(type = Formats.JSONV2)}) + }, + description = "Returns CWMS Rating Data", + tags = {TAG}) + @Override + public void handle(@NotNull Context ctx) throws Exception { + try (final Timer.Context ignored = markAndTime(GET_ONE)) { + String rating = ctx.pathParam(RATING_ID); + + String officeId = ctx.queryParam(OFFICE); + + RatingSet.DatabaseLoadMethod method = ctx.queryParamAsClass(METHOD, + RatingSet.DatabaseLoadMethod.class) + .getOrDefault(RatingSet.DatabaseLoadMethod.EAGER); + + String body = getLatestRatingSet(ctx, method, officeId, rating); + if (body != null) { + ctx.result(body); + ctx.contentType(Formats.JSONV2); + ctx.status(HttpCode.OK); + } + } + } + + private String getLatestRatingSet(Context ctx, RatingSet.DatabaseLoadMethod method, + String officeId, String rating) throws IOException, RatingException { + String ratingSet = null; + try (final Timer.Context ignored = markAndTime("getLatestRatingSet")) { + DSLContext dsl = getDslContext(ctx); + + RatingDao ratingDao = getRatingDao(dsl); + RatingSet returnedSet = ratingDao.retrieveLatest(method, officeId, rating); + ratingSet = JsonRatingUtils.toJson(returnedSet); + } + + return ratingSet; + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingDao.java index 13240adf0..78aef905c 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingDao.java @@ -26,7 +26,6 @@ import hec.data.RatingException; import hec.data.cwmsRating.RatingSet; - import java.io.IOException; import java.time.Instant; import java.util.regex.Matcher; @@ -34,13 +33,16 @@ public interface RatingDao { - static final Pattern officeMatcher = Pattern.compile(".*office-id=\"(.*?)\""); + Pattern officeMatcher = Pattern.compile(".*office-id=\"(.*?)\""); void create(String ratingSet, boolean storeTemplate) throws IOException, RatingException; RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId, String specificationId, Instant start, Instant end) throws IOException, RatingException; + RatingSet retrieveLatest(RatingSet.DatabaseLoadMethod method, String officeId, String specificationId) + throws IOException, RatingException; + String retrieveRatings(String format, String names, String unit, String datum, String office, String start, String end, String timezone); @@ -52,7 +54,7 @@ String retrieveRatings(String format, String names, String unit, String datum, S static String extractOfficeFromXml(String xml) { Matcher officeMatch = officeMatcher.matcher(xml); - if(officeMatch.find()) { + if (officeMatch.find()) { return officeMatch.group(1); } else { throw new RuntimeException("Unable to determine office for data set"); diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java index 37374c40f..1b4958005 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java @@ -24,22 +24,41 @@ package cwms.cda.data.dao; +import static org.jooq.impl.DSL.asterisk; +import static org.jooq.impl.DSL.dateDiff; +import static org.jooq.impl.DSL.field; +import static org.jooq.impl.DSL.inline; +import static org.jooq.impl.DSL.name; +import static org.jooq.impl.DSL.partitionBy; +import static org.jooq.impl.DSL.rank; +import static org.jooq.impl.DSL.select; +import static org.jooq.impl.DSL.toDate; +import static org.jooq.impl.DSL.unquotedName; +import static org.jooq.impl.DSL.val; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import hec.data.RatingException; import hec.data.cwmsRating.RatingSet; +import java.io.IOException; +import java.sql.Connection; +import java.sql.Date; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.List; import mil.army.usace.hec.cwms.rating.io.jdbc.ConnectionProvider; import mil.army.usace.hec.cwms.rating.io.jdbc.RatingJdbcFactory; import org.jooq.DSLContext; +import org.jooq.DatePart; +import org.jooq.Name; +import org.jooq.Record; import org.jooq.exception.DataAccessException; import usace.cwms.db.jooq.codegen.packages.CWMS_RATING_PACKAGE; +import usace.cwms.db.jooq.codegen.tables.AV_RATING_LOCAL; -import java.io.IOException; -import java.sql.Connection; -import java.sql.Timestamp; -import java.time.Instant; -import java.util.List; public class RatingSetDao extends JooqDao implements RatingDao { @@ -57,8 +76,7 @@ public void create(String ratingSetXml, boolean storeTemplate) throws IOExceptio DSLContext context = getDslContext(c, office); String errs = CWMS_RATING_PACKAGE.call_STORE_RATINGS_XML__5(context.configuration(), ratingSetXml, "T", storeTemplate ? "T" : "F"); - if (errs != null && !errs.isEmpty()) - { + if (errs != null && !errs.isEmpty()) { throw new DataAccessException(errs); } }); @@ -83,11 +101,96 @@ private static String extractOfficeId(String ratingSet) throws JsonProcessingExc return office; } + public RatingSet retrieveLatest(RatingSet.DatabaseLoadMethod method, String officeId, + String specificationId) throws IOException, RatingException { + AV_RATING_LOCAL view = AV_RATING_LOCAL.AV_RATING_LOCAL; + + final RatingSet[] retval = new RatingSet[1]; + try { + + if (method == null) { + method = RatingSet.DatabaseLoadMethod.EAGER; + } + + RatingSet.DatabaseLoadMethod finalMethod = method; + + ZoneId utcZone = ZoneId.of("UTC"); + Name rank = unquotedName("rank"); + Name alias1 = unquotedName("a"); + Name alias2 = unquotedName("b"); + Name alias3 = unquotedName("c"); + Name alias4 = unquotedName("d"); + Name diff = unquotedName("difference"); + Name effectiveDateName = unquotedName("effective_date"); + Name ratingIdName = unquotedName("rating_id"); + Name activeFlag = unquotedName("active_flag"); + String dateFormat = "dd-MMM-YYYY H:mm:ss"; + String oracleDateFormat = "DD-MON-YYYY HH24:MI:SS"; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat) + .withZone(utcZone); + String formattedDate = formatter.format(Instant.now()); + + String ratingId = null; + Record result = connectionResult(dsl, c -> + dsl.select(asterisk()) + .from( + select( + field(name(alias1, effectiveDateName)), + field(name(alias1, ratingIdName)), + field(name(alias1, activeFlag)), + rank().over(partitionBy(field(alias3)) + .orderBy(field(diff).asc())) + .as(rank) + ) + .from( + select( + field(name(effectiveDateName)), + field(name(alias2, ratingIdName)), + field(name(alias2, ratingIdName)).as(alias3), + field(name(alias2, activeFlag)), + dateDiff(DatePart.DAY, + field(name(effectiveDateName)).cast(Date.class), + toDate(formattedDate, inline(oracleDateFormat))) + .as(diff)) + .from(view.as(alias2)) + .asTable(alias1).where(field(diff).ge(val(0)))).asTable(alias4)) + .where(field(rank).eq(inline(1)).and(field(activeFlag).eq("T"))) + .fetchOne() + ); + Timestamp effectiveDate = result.getValue(field(name(alias4, effectiveDateName).unquotedName()), + Timestamp.class); + ratingId = result.getValue(field(name(alias4, ratingIdName).unquotedName()), String.class); + + if (ratingId == null) { + return null; + } + Instant effectiveInstant = Instant.parse( + String.format("%sZ", effectiveDate.toString().replace(" ", "T"))); + final long finalStartTime = effectiveInstant.toEpochMilli(); + final String ratingIdFinal = ratingId; + final long finalEndTime = effectiveInstant.toEpochMilli(); + connection(dsl, c -> retval[0] = + RatingJdbcFactory.ratingSet(finalMethod, new RatingConnectionProvider(c), officeId, + ratingIdFinal, finalStartTime, + finalEndTime, false)); + } catch (DataAccessException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof RatingException) { + if (cause.getMessage().contains("contains no rating templates")) { + return null; + } + + throw (RatingException) cause; + } + throw new IOException("Failed to retrieve Rating", ex); + } + return retval[0]; + } + @Override public RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId, String specificationId, Instant startZdt, Instant endZdt ) throws IOException, RatingException { - final RatingSet[] retval = new RatingSet[1]; try { final Long start; @@ -112,7 +215,8 @@ public RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId, connection(dsl, c -> retval[0] = RatingJdbcFactory.ratingSet(finalMethod, new RatingConnectionProvider(c), officeId, - specificationId, start, end, false)); + specificationId, start, end, false)); + } catch (DataAccessException ex) { Throwable cause = ex.getCause(); @@ -151,7 +255,7 @@ public void store(String ratingSetXml, boolean includeTemplate) throws IOExcepti public void delete(String officeId, String specificationId, Instant start, Instant end) { Timestamp startDate = new Timestamp(start.toEpochMilli()); Timestamp endDate = new Timestamp(end.toEpochMilli()); - dsl.connection(c-> + dsl.connection(c -> CWMS_RATING_PACKAGE.call_DELETE_RATINGS( getDslContext(c,officeId).configuration(), specificationId, startDate, endDate, "UTC", officeId @@ -170,15 +274,15 @@ public String retrieveRatings(String format, String names, String unit, String d } private static final class RatingConnectionProvider implements ConnectionProvider { - private final Connection c; + private final Connection conn; - private RatingConnectionProvider(Connection c) { - this.c = c; + private RatingConnectionProvider(Connection conn) { + this.conn = conn; } @Override public Connection getConnection() { - return c; + return conn; } @Override diff --git a/cwms-data-api/src/test/java/cwms/cda/api/RatingsControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/RatingsControllerTestIT.java index 2a55eb8c0..9e93e292b 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/RatingsControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/RatingsControllerTestIT.java @@ -7,34 +7,38 @@ package cwms.cda.api; +import cwms.cda.data.dao.JooqDao; import cwms.cda.formatters.Formats; import fixtures.TestAccounts; import hec.data.cwmsRating.io.RatingSetContainer; import hec.data.cwmsRating.io.RatingSpecContainer; import io.restassured.filter.log.LogDetail; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; import mil.army.usace.hec.cwms.rating.io.xml.RatingContainerXmlFactory; import mil.army.usace.hec.cwms.rating.io.xml.RatingSetContainerXmlFactory; import mil.army.usace.hec.cwms.rating.io.xml.RatingSpecXmlFactory; +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.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import javax.servlet.http.HttpServletResponse; -import java.util.Arrays; - -import static cwms.cda.api.Controllers.FORMAT; -import static cwms.cda.api.Controllers.OFFICE; -import static cwms.cda.api.Controllers.STORE_TEMPLATE; +import static cwms.cda.api.Controllers.*; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; @Tag("integration") -public class RatingsControllerTestIT extends DataApiTestIT +class RatingsControllerTestIT extends DataApiTestIT { private static final String EXISTING_LOC = "RatingsControllerTestIT"; private static final String EXISTING_SPEC = EXISTING_LOC + ".Stage;Flow.COE.Production"; + private static final String TEMPLATE = "Stage;Flow.COE"; private static final String SPK = "SPK"; @BeforeAll @@ -45,7 +49,11 @@ static void beforeAll() throws Exception String ratingXml = readResourceFile("cwms/cda/api/Zanesville_Stage_Flow_COE_Production.xml"); ratingXml = ratingXml.replaceAll("Zanesville", EXISTING_LOC); + String ratingXml2 = ratingXml.replaceAll("2002-04-09T13:53:01Z", "2016-06-06T00:00:00Z"); + String ratingXml3 = ratingXml.replaceAll("2002-04-09T13:53:01Z", "2025-06-06T00:00:00Z"); RatingSetContainer container = RatingSetContainerXmlFactory.ratingSetContainerFromXml(ratingXml); + RatingSetContainer container2 = RatingSetContainerXmlFactory.ratingSetContainerFromXml(ratingXml2); + RatingSetContainer container3 = RatingSetContainerXmlFactory.ratingSetContainerFromXml(ratingXml3); RatingSpecContainer specContainer = container.ratingSpecContainer; specContainer.officeId = SPK; specContainer.specOfficeId = SPK; @@ -53,6 +61,8 @@ static void beforeAll() throws Exception String specXml = RatingSpecXmlFactory.toXml(specContainer, "", 0, true); String templateXml = RatingSpecXmlFactory.toXml(specContainer, "", 0); String setXml = RatingContainerXmlFactory.toXml(container, "", 0, true, false); + String setXml2 = RatingContainerXmlFactory.toXml(container2, "", 0, true, false); + String setXml3 = RatingContainerXmlFactory.toXml(container3, "", 0, true, false); TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; //Create Template @@ -103,6 +113,62 @@ static void beforeAll() throws Exception .assertThat() .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(HttpServletResponse.SC_OK)); + + //Create the second set + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.XMLV2) + .body(setXml2) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, SPK) + .queryParam(STORE_TEMPLATE, false) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/ratings") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // Create the third set + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.XMLV2) + .body(setXml3) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, SPK) + .queryParam(STORE_TEMPLATE, false) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/ratings") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + } + + @AfterAll + static void afterAll() + { + TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + + // Delete Template + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.XMLV2) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, SPK) + .queryParam(METHOD, JooqDao.DeleteMethod.DELETE_ALL) + .when() + .redirects().follow(true) + .redirects().max(3) + .delete("/ratings/template/" + TEMPLATE) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NO_CONTENT)); } @ParameterizedTest @@ -111,7 +177,7 @@ void test_getAll_legacy(GetAllLegacyTest test) { given() .log().ifValidationFails(LogDetail.ALL,true) - .queryParam(FORMAT, test._queryParam) + .queryParam(FORMAT, test.queryParam) .when() .redirects().follow(true) .redirects().max(3) @@ -120,7 +186,7 @@ void test_getAll_legacy(GetAllLegacyTest test) .assertThat() .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(HttpServletResponse.SC_OK)) - .contentType(is(test._expectedContentType)); + .contentType(is(test.expectedContentType)); } @ParameterizedTest @@ -129,7 +195,7 @@ void test_getAll(GetAllTest test) { given() .log().ifValidationFails(LogDetail.ALL,true) - .accept(test._accept) + .accept(test.accept) .when() .redirects().follow(true) .redirects().max(3) @@ -138,7 +204,7 @@ void test_getAll(GetAllTest test) .assertThat() .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(HttpServletResponse.SC_OK)) - .contentType(is(test._expectedContentType)); + .contentType(is(test.expectedContentType)); } @ParameterizedTest @@ -147,7 +213,7 @@ void test_getOne(GetOneTest test) { given() .log().ifValidationFails(LogDetail.ALL,true) - .accept(test._accept) + .accept(test.accept) .queryParam(OFFICE, SPK) .when() .redirects().follow(true) @@ -157,7 +223,32 @@ void test_getOne(GetOneTest test) .assertThat() .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(HttpServletResponse.SC_OK)) - .contentType(is(test._expectedContentType)); + .contentType(is(test.expectedContentType)); + } + + @Test + void test_get_one_latest() { + ExtractableResponse response = given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.JSONV2) + .queryParam(OFFICE, SPK) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/ratings/" + EXISTING_SPEC + "/latest") + .then() + .assertThat() + .log().ifValidationFails(LogDetail.ALL,true) + .statusCode(is(HttpServletResponse.SC_OK)) + .contentType(is(Formats.JSONV2)) + .extract(); + + String effectiveDate = response.path("ratings.simple-rating[0].effective-date"); + if (effectiveDate == null) { + effectiveDate = response.path("simple-rating.effective-date"); + } + assertNotNull(effectiveDate); + assertEquals("2016-06-06T00:00:00Z", effectiveDate); } enum GetOneTest @@ -169,13 +260,13 @@ enum GetOneTest JSONV2(Formats.JSONV2, Formats.JSONV2), ; - final String _accept; - final String _expectedContentType; + final String accept; + final String expectedContentType; GetOneTest(String accept, String expectedContentType) { - _accept = accept; - _expectedContentType = expectedContentType; + this.accept = accept; + this.expectedContentType = expectedContentType; } } @@ -185,13 +276,13 @@ enum GetAllLegacyTest XML(Formats.XML_LEGACY, Formats.XML), ; - final String _queryParam; - final String _expectedContentType; + final String queryParam; + final String expectedContentType; GetAllLegacyTest(String queryParam, String expectedContentType) { - _queryParam = queryParam; - _expectedContentType = expectedContentType; + this.queryParam = queryParam; + this.expectedContentType = expectedContentType; } } @@ -206,13 +297,13 @@ enum GetAllTest JSONV2(Formats.JSONV2, Formats.JSONV2), ; - final String _accept; - final String _expectedContentType; + final String accept; + final String expectedContentType; GetAllTest(String accept, String expectedContentType) { - _accept = accept; - _expectedContentType = expectedContentType; + this.accept = accept; + this.expectedContentType = expectedContentType; } } }