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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@
import cwms.cda.api.Controllers;
import cwms.cda.api.errors.CdaError;
import cwms.cda.data.dao.JooqDao;
import cwms.cda.data.dao.JsonRatingUtils;
import cwms.cda.data.dao.RatingSpecDao;
import cwms.cda.data.dto.rating.RatingSpec;
import cwms.cda.data.dto.rating.RatingSpecs;
import cwms.cda.formatters.ContentType;
import cwms.cda.formatters.Formats;
import cwms.cda.formatters.FormattingException;
import io.javalin.apibuilder.CrudHandler;
import io.javalin.core.util.Header;
import io.javalin.http.Context;
Expand All @@ -48,11 +48,13 @@
import io.javalin.plugin.openapi.annotations.OpenApiParam;
import io.javalin.plugin.openapi.annotations.OpenApiRequestBody;
import io.javalin.plugin.openapi.annotations.OpenApiResponse;
import java.io.IOException;

import java.util.Optional;

import com.google.common.flogger.FluentLogger;

import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.TransformerException;

import org.jetbrains.annotations.NotNull;
import org.jooq.DSLContext;

Expand Down Expand Up @@ -100,13 +102,14 @@ private Timer.Context markAndTime(String subject) {
),
@OpenApiParam(name = PAGE_SIZE, type = Integer.class,
description = "How many entries per page returned. "
+ "Default " + DEFAULT_PAGE_SIZE + "."
+ "Default " + DEFAULT_PAGE_SIZE + "."
),
},
responses = {
@OpenApiResponse(status = STATUS_200,
content = {
@OpenApiContent(type = Formats.JSONV2, from = RatingSpecs.class)
@OpenApiContent(type = Formats.JSONV2, from = RatingSpecs.class),
@OpenApiContent(type = Formats.XMLV2, from = RatingSpecs.class)
}
)},
tags = {TAG}
Expand All @@ -122,7 +125,7 @@ public void getAll(Context ctx) {

String formatHeader = ctx.header(Header.ACCEPT);
ContentType contentType = Formats.parseHeader(formatHeader, RatingSpecs.class);
try (final Timer.Context timeContext = markAndTime(GET_ALL)){
try (final Timer.Context ignored = markAndTime(GET_ALL)) {
DSLContext dsl = getDslContext(ctx);

RatingSpecDao ratingSpecDao = getRatingSpecDao(dsl);
Expand Down Expand Up @@ -159,6 +162,7 @@ public void getAll(Context ctx) {
@OpenApiResponse(status = STATUS_200,
content = {
@OpenApiContent(from = RatingSpec.class, type = Formats.JSONV2),
@OpenApiContent(from = RatingSpec.class, type = Formats.XMLV2)
}
)
},
Expand All @@ -171,7 +175,7 @@ public void getOne(Context ctx, String ratingId) {

String office = ctx.queryParam(OFFICE);

try (final Timer.Context timeContext = markAndTime(GET_ONE)){
try (final Timer.Context ignored = markAndTime(GET_ONE)) {
DSLContext dsl = getDslContext(ctx);

RatingSpecDao ratingSpecDao = getRatingSpecDao(dsl);
Expand Down Expand Up @@ -201,58 +205,50 @@ protected RatingSpecDao getRatingSpecDao(DSLContext dsl) {


@OpenApi(
description = "Create new Rating Specification",
requestBody = @OpenApiRequestBody(
content = {
@OpenApiContent(from = RatingSpec.class, type = Formats.XMLV2)
description = "Create new Rating Specification",
requestBody = @OpenApiRequestBody(
content = {
@OpenApiContent(from = RatingSpec.class, type = Formats.JSON),
@OpenApiContent(from = RatingSpec.class, type = Formats.XMLV2)
},
required = true),
queryParams = {
@OpenApiParam(name = FAIL_IF_EXISTS, type = Boolean.class,
description = "Create will fail if provided ID already exists. Default: true")
},
required = true),
queryParams = {
@OpenApiParam(name = FAIL_IF_EXISTS, type = Boolean.class,
description = "Create will fail if provided ID already exists. Default: true")
},
method = HttpMethod.POST,
tags = {TAG}
method = HttpMethod.POST,
tags = {TAG}
)
@Override
public void create(Context ctx) {
try (final Timer.Context ignored = markAndTime(CREATE)){
try (final Timer.Context ignored = markAndTime(CREATE)) {
DSLContext dsl = getDslContext(ctx);

String reqContentType = ctx.req.getContentType();
String formatHeader = reqContentType != null ? reqContentType : Formats.XMLV2;
String contentTypeHeader = ctx.req.getContentType();
String body = ctx.body();
String xml = translateToXml(body, formatHeader);
RatingSpecDao dao = new RatingSpecDao(dsl);
boolean failIfExists = ctx.queryParamAsClass(FAIL_IF_EXISTS, Boolean.class).getOrDefault(false);
dao.create(xml, failIfExists);
ctx.status(HttpServletResponse.SC_CREATED);
}
}
ContentType contentType = Formats.parseHeader(contentTypeHeader, RatingSpec.class);

private static String translateToXml(String body, String contentType) {
String retval;
boolean failIfExists = ctx.queryParamAsClass(FAIL_IF_EXISTS, Boolean.class).getOrDefault(false);
RatingSpecDao dao = new RatingSpecDao(dsl);

if (contentType.contains(Formats.XMLV2)) {
retval = body;
} else if (contentType.contains(Formats.JSONV2)) {
retval = translateJsonToXml(body);
} else {
throw new IllegalArgumentException("Unexpected contentType format:" + contentType);
try {
RatingSpec spec = Formats.parseContent(contentType, body, RatingSpec.class);
// If we can parse it into our CDA RatingSpec object have the DAO use it.
dao.create(spec, failIfExists);
ctx.status(HttpServletResponse.SC_CREATED);
} catch (FormattingException e) {
if (contentType.getType().contains(Formats.XML)) {
// The user said its xml but it doesn't parse into our CDA RatingSpec object.
// We'll let the dao try doing a string pass-thru to the pl/sql.
dao.create(body, failIfExists);
ctx.status(HttpServletResponse.SC_CREATED);
return;
}
throw e;
}
}

return retval;
}

private static String translateJsonToXml(String body) {
String retval;
try {
retval = JsonRatingUtils.jsonToXml(body);
} catch (IOException | TransformerException ex) {
throw new IllegalArgumentException("Failed to translate request into rating spec XML", ex);
}
return retval;
}

@OpenApi(ignore = true)
@Override
Expand All @@ -261,22 +257,22 @@ public void update(Context ctx, String locationCode) {
}

@OpenApi(
pathParams = {
@OpenApiParam(name = RATING_ID, required = true, description = "The rating-spec-id of the ratings data to be deleted."),
},
queryParams = {
@OpenApiParam(name = OFFICE, required = true, description = "Specifies the "
+ "owning office of the ratings to be deleted."),
@OpenApiParam(name = METHOD, required = true, description = "Specifies the delete method used.",
type = JooqDao.DeleteMethod.class)
},
description = "Deletes requested rating specification",
method = HttpMethod.DELETE,
tags = {TAG}
pathParams = {
@OpenApiParam(name = RATING_ID, required = true, description = "The rating-spec-id of the ratings data to be deleted."),
},
queryParams = {
@OpenApiParam(name = OFFICE, required = true, description = "Specifies the "
+ "owning office of the ratings to be deleted."),
@OpenApiParam(name = METHOD, required = true, description = "Specifies the delete method used.",
type = JooqDao.DeleteMethod.class)
},
description = "Deletes requested rating specification",
method = HttpMethod.DELETE,
tags = {TAG}
)
@Override
public void delete(Context ctx, @NotNull String ratingSpecId) {
try (final Timer.Context ignored = markAndTime(DELETE)){
try (final Timer.Context ignored = markAndTime(DELETE)) {
DSLContext dsl = getDslContext(ctx);

String office = ctx.queryParam(OFFICE);
Expand Down
66 changes: 44 additions & 22 deletions cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSpecDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
import static cwms.cda.data.dto.rating.RatingSpec.Builder.buildIndependentRoundingSpecs;
import static java.util.stream.Collectors.toList;

import com.fasterxml.jackson.core.JsonProcessingException;
import cwms.cda.data.dto.CwmsDTOPaginated;
import cwms.cda.data.dto.rating.RatingEffectiveDatesMap;
import cwms.cda.data.dto.rating.RatingSpec;
import cwms.cda.data.dto.rating.RatingSpecEffectiveDates;
import cwms.cda.data.dto.rating.RatingSpecs;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
Expand All @@ -58,11 +60,15 @@
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;

import com.google.common.flogger.FluentLogger;

import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetProvider;

import cwms.cda.formatters.FormattingException;
import org.jetbrains.annotations.NotNull;
import org.jooq.Condition;
import org.jooq.DSLContext;
Expand Down Expand Up @@ -193,7 +199,7 @@ public RatingSpecs retrieveRatingSpecs(String cursor, int pageSize, String offic
Set<RatingSpec> retval = getRatingSpecs(office, specIdMask, offset, pageSize);

RatingSpecs.Builder builder = new RatingSpecs.Builder(offset, pageSize, total);
builder.specs(new ArrayList<>(retval));
builder.withSpecs(new ArrayList<>(retval));
return builder.build();
}

Expand Down Expand Up @@ -388,7 +394,7 @@ public static RatingSpec buildRatingSpec(Record rec) {

public void delete(String office, DeleteMethod deleteMethod, String ratingSpecId) {
String deleteAction;
switch(deleteMethod) {
switch (deleteMethod) {
case DELETE_ALL:
deleteAction = DeleteRule.DELETE_ALL.getRule();
break;
Expand All @@ -400,24 +406,41 @@ public void delete(String office, DeleteMethod deleteMethod, String ratingSpecId
break;
default:
throw new IllegalArgumentException("Delete Method provided does not match accepted rule constants: "
+ deleteMethod);
+ deleteMethod);
}
dsl.connection(c ->
CWMS_RATING_PACKAGE.call_DELETE_SPECS(
getDslContext(c,office).configuration(),
ratingSpecId,
deleteAction,
office)
CWMS_RATING_PACKAGE.call_DELETE_SPECS(
getDslContext(c, office).configuration(),
ratingSpecId,
deleteAction,
office)
);
}

public void create(RatingSpec spec, boolean failIfExists) {
String xml = null;
try {
xml = RatingSpecXmlUtils.toPlSqlXml(spec);
create(xml, failIfExists);
} catch (JsonProcessingException ex) {
String msg = spec != null ?
"Error rendering '" + spec + "' to XML"
:
"Null element passed to formatter";
logger.atWarning().withCause(ex).log(msg);
throw new FormattingException(msg, ex);
}
}

// In my tests this method wouldn't fail if the input was
// mostly right, it just wouldn't create anything.
public void create(String xml, boolean failIfExists) {
final String office = RatingDao.extractOfficeFromXml(xml);
dsl.connection(c ->
CWMS_RATING_PACKAGE.call_STORE_SPECS__3(
getDslContext(c,office).configuration(),
xml,
formatBool(failIfExists))
CWMS_RATING_PACKAGE.call_STORE_SPECS__3(
getDslContext(c, office).configuration(),
xml,
formatBool(failIfExists))
);
}

Expand All @@ -432,21 +455,21 @@ public RatingEffectiveDatesMap retrieveSpecEffectiveDates(String officeIdMask, S
//office->spec->dates
NavigableMap<String, NavigableMap<String, NavigableSet<Instant>>> specDateMap = new TreeMap<>();
//instantiate empty Instant list for each office/spec combination so that specs with no effective dates are included in the final result
for(Map.Entry<String, List<String>> entry : officeToRatingIdsNoAliasesMap.entrySet()) {
for (Map.Entry<String, List<String>> entry : officeToRatingIdsNoAliasesMap.entrySet()) {
String officeId = entry.getKey();
List<String> specIds = entry.getValue();
NavigableMap<String, NavigableSet<Instant>> specMap = specDateMap.computeIfAbsent(officeId, k -> new TreeMap<>());
for(String specId : specIds) {
for (String specId : specIds) {
specMap.put(specId, new TreeSet<>());
}
}
try(ResultSet rs = catRatings(conn, officeIdMask, specIdMask, begin, end)) {
try (ResultSet rs = catRatings(conn, officeIdMask, specIdMask, begin, end)) {
checkMetaData(rs.getMetaData(), RATINGS_COLUMN_LIST, "Ratings");
while(rs.next()) {
while (rs.next()) {
String officeId = rs.getString(OFFICE_ID);
String specId = rs.getString(SPECIFICATION_ID);
List<String> ratingIdsNoAliases = officeToRatingIdsNoAliasesMap.get(officeId);
if(ratingIdsNoAliases != null && !ratingIdsNoAliases.contains(specId)) { // skip aliased specs based on queried list of rating ids not including aliases
if (ratingIdsNoAliases != null && !ratingIdsNoAliases.contains(specId)) { // skip aliased specs based on queried list of rating ids not including aliases
continue;
}
Timestamp timestamp = rs.getTimestamp(EFFECTIVE_DATE, GMT_CALENDAR);
Expand All @@ -463,11 +486,11 @@ public RatingEffectiveDatesMap retrieveSpecEffectiveDates(String officeIdMask, S
//package scoped for unit testing
static RatingEffectiveDatesMap buildRatingEffectiveDatesMap(NavigableMap<String, NavigableMap<String, NavigableSet<Instant>>> specDateMap) {
Map<String, List<RatingSpecEffectiveDates>> officeToSpecDatesMap = new LinkedHashMap<>(specDateMap.size());
for(Map.Entry<String, NavigableMap<String, NavigableSet<Instant>>> entry : specDateMap.entrySet()) {
for (Map.Entry<String, NavigableMap<String, NavigableSet<Instant>>> entry : specDateMap.entrySet()) {
String officeId = entry.getKey();
List<RatingSpecEffectiveDates> specEffectiveDatesForOffice = new ArrayList<>();
NavigableMap<String, NavigableSet<Instant>> specMap = entry.getValue();
for(Map.Entry<String, NavigableSet<Instant>> specEntry : specMap.entrySet()) {
for (Map.Entry<String, NavigableSet<Instant>> specEntry : specMap.entrySet()) {
String specId = specEntry.getKey();
NavigableSet<Instant> dateList = specEntry.getValue();
if (dateList.isEmpty()) {
Expand Down Expand Up @@ -495,8 +518,7 @@ private ResultSet catRatings(Connection conn, String officeIdMask, String specId
CachedRowSet output = RowSetProvider.newFactory()
.createCachedRowSet();

try (CallableStatement statement = conn.prepareCall("{CALL CWMS_20.CWMS_RATING.CAT_RATINGS(?, ?, ?, ?, ?, ?)}"))
{
try (CallableStatement statement = conn.prepareCall("{CALL CWMS_20.CWMS_RATING.CAT_RATINGS(?, ?, ?, ?, ?, ?)}")) {
statement.registerOutParameter(1, Types.REF_CURSOR);
statement.setString(2, specIdMask);
statement.setTimestamp(3, pEffectiveDateStart, GMT_CALENDAR);
Expand Down Expand Up @@ -527,7 +549,7 @@ private Map<String, List<String>> getRatingIds(String office, String templateIdM
condition = condition.and(ratingIdLike);
}

if(!includeAliases) {
if (!includeAliases) {
condition = condition.and(specView.ALIASED_ITEM.isNull());
}

Expand Down
Loading
Loading