diff --git a/cwms-data-api/src/main/java/cwms/cda/api/BaseCrudHandler.java b/cwms-data-api/src/main/java/cwms/cda/api/BaseCrudHandler.java index 96d813cf3..d6b2e7f75 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/BaseCrudHandler.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/BaseCrudHandler.java @@ -23,12 +23,15 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import com.google.common.flogger.FluentLogger; import io.javalin.apibuilder.CrudHandler; +import io.javalin.http.Context; import static com.codahale.metrics.MetricRegistry.name; import static cwms.cda.api.Controllers.RESULTS; import static cwms.cda.api.Controllers.SIZE; public abstract class BaseCrudHandler implements CrudHandler { + private static final FluentLogger LOGGER = FluentLogger.forEnclosingClass(); private final MetricRegistry metrics; private final Histogram requestResultSize; @@ -46,4 +49,21 @@ protected final Timer.Context markAndTime(String subject) { protected final void updateResultSize(String responseString) { requestResultSize.update(responseString.length()); } + + protected final void updateResultSize(int responseLength) { + requestResultSize.update(responseLength); + } + + protected final void updateResultSize(long responseLength) { + requestResultSize.update(responseLength); + } + + public MetricRegistry getMetrics() { + return metrics; + } + + protected final void logUnusedPathParameter(Context ctx, String pathParam, String reason) { + String param = ctx.pathParam(pathParam); + LOGGER.atFinest().log("Path parameter '%s' is documented but not used in handler '%s'\nValue: '%s'\nReason: '%s'", pathParam, this.getClass().getSimpleName(), param, reason); + } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/BaseHandler.java b/cwms-data-api/src/main/java/cwms/cda/api/BaseHandler.java index 0a4ce454d..db9dc01db 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/BaseHandler.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/BaseHandler.java @@ -23,6 +23,8 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import com.google.common.flogger.FluentLogger; +import io.javalin.http.Context; import io.javalin.http.Handler; import static com.codahale.metrics.MetricRegistry.name; import static cwms.cda.api.Controllers.RESULTS; @@ -30,6 +32,7 @@ public abstract class BaseHandler implements Handler { + private static final FluentLogger LOGGER = FluentLogger.forEnclosingClass(); private final MetricRegistry metrics; private final Histogram requestResultSize; @@ -47,4 +50,13 @@ protected final Timer.Context markAndTime(String subject) { protected final void updateResultSize(int value) { requestResultSize.update(value); } + + protected final void updateResultSize(long value) { + requestResultSize.update(value); + } + + protected final void logUnusedPathParameter(Context ctx, String pathParam, String reason) { + String param = ctx.pathParam(pathParam); + LOGGER.atFinest().log("Path parameter '%s' is documented but not used in handler '%s'\nValue: '%s'\nReason: '%s'", pathParam, this.getClass().getSimpleName(), param, reason); + } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/BinaryTimeSeriesController.java b/cwms-data-api/src/main/java/cwms/cda/api/BinaryTimeSeriesController.java index 767f1ed36..8f6f1d0e1 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/BinaryTimeSeriesController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/BinaryTimeSeriesController.java @@ -70,19 +70,18 @@ import org.jooq.DSLContext; -public class BinaryTimeSeriesController implements CrudHandler { +public class BinaryTimeSeriesController extends BaseCrudHandler { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); static final String TAG = "Binary-TimeSeries"; public static final String REPLACE_ALL = "replace-all"; private static final String DEFAULT_BIN_TYPE_MASK = "*"; public static final String BINARY_TYPE_MASK = "binary-type-mask"; - private final MetricRegistry metrics; public BinaryTimeSeriesController(MetricRegistry metrics) { - this.metrics = metrics; + super(metrics); } @NotNull @@ -91,11 +90,6 @@ protected TimeSeriesBinaryDao getDao(DSLContext dsl) { } - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); - } - - @OpenApi( summary = "Retrieve binary time series values for a provided time window and date version." + "If individual values exceed 64 kilobytes, a URL to a separate download is " @@ -179,7 +173,7 @@ public void getAll(@NotNull Context ctx) { @OpenApi(ignore = true) @Override public void getOne(@NotNull Context ctx, @NotNull String templateId) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( @@ -232,8 +226,8 @@ public void create(@NotNull Context ctx) { tags = {TAG} ) @Override - public void update(@NotNull Context ctx, @NotNull String oldBinaryTimeSeriesId) { - + public void update(@NotNull Context ctx, @NotNull String name) { + logUnusedPathParameter(ctx, NAME, "Body contains information"); try (Timer.Context ignored = markAndTime(UPDATE)) { boolean maxVersion = true; boolean replaceAll = ctx.queryParamAsClass(REPLACE_ALL, Boolean.class).getOrDefault(false); @@ -276,7 +270,7 @@ public void update(@NotNull Context ctx, @NotNull String oldBinaryTimeSeriesId) tags = {TAG} ) @Override - public void delete(@NotNull Context ctx, @NotNull String binaryTimeSeriesId) { + public void delete(@NotNull Context ctx, @NotNull String name) { try (Timer.Context ignored = markAndTime(DELETE)) { DSLContext dsl = getDslContext(ctx); String office = requiredParam(ctx, OFFICE); @@ -289,7 +283,7 @@ public void delete(@NotNull Context ctx, @NotNull String binaryTimeSeriesId) { TimeSeriesBinaryDao dao = getDao(dsl); - dao.delete(office, binaryTimeSeriesId, mask, begin, end, version); + dao.delete(office, name, mask, begin, end, version); ctx.status(HttpServletResponse.SC_NO_CONTENT); } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/BinaryTimeSeriesValueController.java b/cwms-data-api/src/main/java/cwms/cda/api/BinaryTimeSeriesValueController.java index fb66e766c..cffac4407 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/BinaryTimeSeriesValueController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/BinaryTimeSeriesValueController.java @@ -27,6 +27,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import com.google.common.flogger.FluentLogger; import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.BlobDao; import cwms.cda.data.dao.StreamConsumer; @@ -47,18 +48,11 @@ import static cwms.cda.data.dao.JooqDao.getDslContext; -public class BinaryTimeSeriesValueController implements Handler { - private final MetricRegistry metrics; - private final Histogram requestResultSize; - +public class BinaryTimeSeriesValueController extends BaseHandler { + private static final FluentLogger LOGGER = FluentLogger.forEnclosingClass(); public BinaryTimeSeriesValueController(MetricRegistry metrics) { - this.metrics = metrics; - requestResultSize = this.metrics.histogram((name(BinaryTimeSeriesValueController.class, RESULTS, SIZE))); - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } @OpenApi( @@ -69,12 +63,6 @@ private Timer.Context markAndTime(String subject) { queryParams = { @OpenApiParam(name = OFFICE, required = true, description = "Specifies the owning office of " + "the Binary TimeSeries whose data is 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). If this field is not specified, " - + "the default time zone of UTC shall be used."), - @OpenApiParam(name = DATE, required = true, description = "The date of the binary value to retrieve"), - @OpenApiParam(name = VERSION_DATE, description = "The version date for the value to retrieve."), @OpenApiParam(name = BLOB_ID, description = "Will be removed in a schema update. " + "This is a placeholder for integration testing with schema 23.3.16", deprecated = true) }, @@ -89,6 +77,8 @@ private Timer.Context markAndTime(String subject) { public void handle(@NotNull Context ctx) { //Implementation will change with new CWMS schema //https://www.hec.usace.army.mil/confluence/display/CWMS/2024-02-29+Task2A+Text-ts+and+Binary-ts+Design + logUnusedPathParameter(ctx, NAME, "Handled as " + BLOB_ID + " in query parameter. May change with schema."); + try (Timer.Context ignored = markAndTime(GET_ALL)) { String binaryId = requiredParam(ctx, BLOB_ID); String officeId = requiredParam(ctx, OFFICE); @@ -113,7 +103,7 @@ public void handle(@NotNull Context ctx) { ctx.status(HttpServletResponse.SC_NOT_FOUND).json(new CdaError("Unable to find " + "blob based on given parameters")); } else { - requestResultSize.update(totalLength); + updateResultSize(totalLength); RangeRequestUtil.seekableStream(ctx, is, isPosition, mediaType, totalLength); } }; diff --git a/cwms-data-api/src/main/java/cwms/cda/api/BlobController.java b/cwms-data-api/src/main/java/cwms/cda/api/BlobController.java index 5470c1d97..65e3aaa0f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/BlobController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/BlobController.java @@ -40,24 +40,12 @@ /** * */ -public class BlobController implements CrudHandler { +public class BlobController extends BaseCrudHandler { private static final int DEFAULT_PAGE_SIZE = 20; public static final String TAG = "Blob"; - private final MetricRegistry metrics; - - - private final Histogram requestResultSize; - public BlobController(MetricRegistry metrics) { - this.metrics = metrics; - String className = BlobController.class.getName(); - - requestResultSize = this.metrics.histogram((name(className, RESULTS, SIZE))); - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } protected DSLContext getDslContext(Context ctx) { @@ -91,6 +79,11 @@ private BlobAccess chooseBlobAccess(DSLContext dsl) { + "identifies where in the request you are. This is an opaque" + " value, and can be obtained from the 'next-page' value in " + "the response."), + @OpenApiParam(name = CURSOR, deprecated = true, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response. Deprecated, use " + PAGE + " instead."), @OpenApiParam(name = PAGE_SIZE, type = Integer.class, description = "How many entries per page returned. Default " @@ -116,7 +109,7 @@ public void getAll(@NotNull Context ctx) { String office = ctx.queryParam(OFFICE); String cursor = queryParamAsClass(ctx, new String[]{PAGE, CURSOR}, - String.class, "", metrics, name(BlobController.class.getName(), GET_ALL)); + String.class, "", getMetrics(), name(BlobController.class.getName(), GET_ALL)); if (!CwmsDTOPaginated.CURSOR_CHECK.invoke(cursor)) { ctx.json(new CdaError("cursor or page passed in but failed validation")) @@ -125,7 +118,7 @@ public void getAll(@NotNull Context ctx) { } int pageSize = queryParamAsClass(ctx, new String[]{PAGE_SIZE}, - Integer.class, DEFAULT_PAGE_SIZE, metrics, + Integer.class, DEFAULT_PAGE_SIZE, getMetrics(), name(BlobController.class.getName(), GET_ALL)); String like = ctx.queryParamAsClass(LIKE, String.class).getOrDefault(".*"); @@ -140,13 +133,21 @@ public void getAll(@NotNull Context ctx) { ctx.result(result); ctx.contentType(contentType.toString()); - requestResultSize.update(result.length()); + updateResultSize(result.length()); } } @OpenApi( description = "Returns the binary value of the requested blob as a seekable stream with the " + "appropriate media type.", + pathParams = { + @OpenApiParam(name = BLOB_ID, description = "If the _query_ parameter is provided this _path_ parameter " + + "is ignored and the value of the query parameter is used. " + + "Note: the _query_ parameter is necessary for id's that contain '/' or other special " + + "characters. This is due to limitations in path pattern matching. " + + "We will likely add support for encoding the ID in the path in the future. For now use the id field for those IDs. " + + "Client libraries should detect slashes and choose the appropriate field. \"ignored\" is suggested for the path endpoint."), + }, queryParams = { @OpenApiParam(name = OFFICE, description = "Specifies the owning office."), @OpenApiParam(name = BLOB_ID, description = "If this _query_ parameter is provided the id _path_ parameter " @@ -198,7 +199,7 @@ public void getOne(@NotNull Context ctx, @NotNull String blobId) { ctx.status(HttpServletResponse.SC_NOT_FOUND).json(new CdaError("Unable to find " + "blob based on given parameters")); } else { - requestResultSize.update(totalLength); + updateResultSize(totalLength); // is OracleBlobInputStream or something from MinIO RangeRequestUtil.seekableStream(ctx, is, isPosition, mediaType, totalLength); } @@ -265,6 +266,8 @@ public void create(@NotNull Context ctx) { ) @Override public void update(@NotNull Context ctx, @NotNull String blobId) { + logUnusedPathParameter(ctx, BLOB_ID, "Body contains information"); + try (final Timer.Context ignored = markAndTime(UPDATE)) { String idQueryParam = ctx.queryParam(BLOB_ID); if (idQueryParam != null) { 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 f21784f0b..a4d04a7e4 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 @@ -31,6 +31,7 @@ import java.util.Map; import java.util.Set; import com.google.common.flogger.FluentLogger; +import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; import org.owasp.html.PolicyFactory; @@ -63,19 +64,19 @@ private Timer.Context markAndTime(String subject) { @OpenApi(tags = {TAG}, ignore = true) @Override public void create(Context ctx) { - ctx.status(HttpCode.NOT_IMPLEMENTED).result("cannot perform this action"); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(tags = {"Catalog"}, ignore = true) @Override public void delete(Context ctx, @NotNull String entry) { - ctx.status(HttpCode.NOT_IMPLEMENTED).result("cannot perform this action"); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(tags = {"Catalog"}, ignore = true) @Override public void getAll(Context ctx) { - ctx.status(HttpCode.NOT_IMPLEMENTED).result("cannot perform this action"); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( @@ -84,7 +85,11 @@ public void getAll(Context ctx) { description = "This end point can return a lot of data, this " + "identifies where in the request you are." ), - + @OpenApiParam(name = CURSOR, deprecated = true, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response. Deprecated, use " + PAGE + " instead."), @OpenApiParam(name = PAGE_SIZE, type = Integer.class, description = "How many entries per page returned. Default 500." @@ -311,7 +316,7 @@ private static void warnAboutNotSupported(@NotNull Context ctx, String[] warnAbo @OpenApi(tags = {"Catalog"}, ignore = true) @Override public void update(Context ctx, @NotNull String entry) { - ctx.status(HttpCode.NOT_IMPLEMENTED).json(CdaError.notImplemented()); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ClobController.java b/cwms-data-api/src/main/java/cwms/cda/api/ClobController.java index 89d858abe..f14a128ac 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ClobController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ClobController.java @@ -86,6 +86,11 @@ protected DSLContext getDslContext(Context ctx) { + "identifies where in the request you are. This is an opaque" + " value, and can be obtained from the 'next-page' value in " + "the response."), + @OpenApiParam(name = CURSOR, deprecated = true, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response. Deprecated, use " + PAGE + " instead."), @OpenApiParam(name = PAGE_SIZE, type = Integer.class, description = "How many entries per page returned. Default " @@ -152,6 +157,14 @@ public void getAll(@NotNull Context ctx) { + "When the accept header is set to " + Formats.JSONV2 + " the clob will be returned as a serialized Clob " + "object with fields for office-id, id, description and value. " + "For more information about accept header usage, see this page.", + pathParams = { + @OpenApiParam(name = CLOB_ID, description = "If the _query_ parameter is provided this _path_ parameter " + + "is ignored and the value of the query parameter is used. " + + "Note: the query parameter is necessary for id's that contain '/' or other special " + + "characters. This is due to limitations in path pattern matching. " + + "We will likely add support for encoding the ID in the path in the future. For now use the id field for those IDs. " + + "Client libraries should detect slashes and choose the appropriate field. \"ignored\" is suggested for the path endpoint."), + }, queryParams = { @OpenApiParam(name = OFFICE, description = "Specifies the owning office."), @OpenApiParam(name = CLOB_ID, description = "If this _query_ parameter is provided the id _path_ parameter " 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 3528a22e7..bd06fab4f 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 @@ -241,6 +241,7 @@ public final class Controllers { private static final String DEPRECATED_CSV = "2024-11-01 CSV is not used often."; public static final String QUERY = "query"; + public static final String INCLUDE_ROLES = "include-roles"; static { diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ForecastFileController.java b/cwms-data-api/src/main/java/cwms/cda/api/ForecastFileController.java index d41274d87..607d59492 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ForecastFileController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ForecastFileController.java @@ -95,7 +95,7 @@ private Timer.Context markAndTime(String subject) { tags = {ForecastSpecController.TAG} ) public void handle(@NotNull Context ctx) { - String specId = requiredParam(ctx, NAME); + String specId = ctx.pathParam(NAME); String office = requiredParam(ctx, OFFICE); String designator = ctx.queryParamAsClass(DESIGNATOR, String.class).allowNullable().get(); String forecastDate = requiredParam(ctx, FORECAST_DATE); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ForecastInstanceController.java b/cwms-data-api/src/main/java/cwms/cda/api/ForecastInstanceController.java index 0899db67d..e7ed5292e 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ForecastInstanceController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ForecastInstanceController.java @@ -33,22 +33,13 @@ import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; -public final class ForecastInstanceController implements CrudHandler { +public final class ForecastInstanceController extends BaseCrudHandler { public static final String TAG = "Forecast"; - private final MetricRegistry metrics; - - private final Histogram requestResultSize; private static final int KILO_BYTE_LIMIT = Integer.parseInt(System.getProperty("cda.api.forecast.file.max.length.kB", "64")); public ForecastInstanceController(MetricRegistry metrics) { - this.metrics = metrics; - String className = this.getClass().getName(); - requestResultSize = this.metrics.histogram((name(className, RESULTS, SIZE))); - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } protected DSLContext getDslContext(Context ctx) { @@ -172,7 +163,7 @@ public void getAll(@NotNull Context ctx) { String result = Formats.format(contentType, instances, ForecastInstance.class); ctx.result(result).contentType(contentType.toString()); - requestResultSize.update(result.length()); + updateResultSize(result.length()); ctx.status(HttpServletResponse.SC_OK); } catch (URISyntaxException e) { @@ -242,7 +233,7 @@ public void getOne(@NotNull Context ctx, @NotNull String name) { String result = Formats.format(contentType, instance); ctx.result(result).contentType(contentType.toString()); - requestResultSize.update(result.length()); + updateResultSize(result.length()); ctx.status(HttpServletResponse.SC_OK); } catch (URISyntaxException e) { @@ -271,6 +262,7 @@ public void getOne(@NotNull Context ctx, @NotNull String name) { ) @Override public void update(@NotNull Context ctx, @NotNull String name) { + logUnusedPathParameter(ctx, NAME, "Body contains information"); try (final Timer.Context ignored = markAndTime(UPDATE)) { ForecastInstance forecastInstance = deserializeForecastInstance(ctx); ForecastInstanceDao dao = new ForecastInstanceDao(getDslContext(ctx)); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ForecastSpecController.java b/cwms-data-api/src/main/java/cwms/cda/api/ForecastSpecController.java index d991dab7b..a856bfa63 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ForecastSpecController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ForecastSpecController.java @@ -1,9 +1,5 @@ package cwms.cda.api; -import static com.codahale.metrics.MetricRegistry.name; -import static cwms.cda.api.Controllers.*; - -import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import cwms.cda.data.dao.DeleteRule; @@ -12,7 +8,6 @@ import cwms.cda.data.dto.forecast.ForecastSpec; import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; -import io.javalin.apibuilder.CrudHandler; import io.javalin.core.util.Header; import io.javalin.http.Context; import io.javalin.plugin.openapi.annotations.HttpMethod; @@ -21,27 +16,18 @@ 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.List; import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; +import static cwms.cda.api.Controllers.*; -public final class ForecastSpecController implements CrudHandler { +public final class ForecastSpecController extends BaseCrudHandler { public static final String TAG = "Forecast"; - private final MetricRegistry metrics; - - private final Histogram requestResultSize; public ForecastSpecController(MetricRegistry metrics) { - this.metrics = metrics; - String className = this.getClass().getName(); - requestResultSize = this.metrics.histogram((name(className, RESULTS, SIZE))); - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } protected DSLContext getDslContext(Context ctx) { @@ -173,7 +159,7 @@ public void getAll(@NotNull Context ctx) { String result = Formats.format(contentType, specs, ForecastSpec.class); ctx.result(result).contentType(contentType.toString()); - requestResultSize.update(result.length()); + updateResultSize(result.length()); ctx.status(HttpServletResponse.SC_OK); } @@ -222,7 +208,7 @@ public void getOne(@NotNull Context ctx, @NotNull String name) { String result = Formats.format(contentType, spec); ctx.result(result).contentType(contentType.toString()); - requestResultSize.update(result.length()); + updateResultSize(result.length()); ctx.status(HttpServletResponse.SC_OK); } @@ -247,6 +233,7 @@ public void getOne(@NotNull Context ctx, @NotNull String name) { ) @Override public void update(@NotNull Context ctx, @NotNull String name) { + logUnusedPathParameter(ctx, NAME, "Body contains information"); try (final Timer.Context ignored = markAndTime(UPDATE)) { ForecastSpec forecastSpec = deserializeForecastSpec(ctx); DSLContext dsl = getDslContext(ctx); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ForecastTimeseriesController.java b/cwms-data-api/src/main/java/cwms/cda/api/ForecastTimeseriesController.java index ce604c26c..4aed7436b 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ForecastTimeseriesController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ForecastTimeseriesController.java @@ -3,6 +3,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.JooqDao; import cwms.cda.data.dto.TimeSeries; import cwms.cda.formatters.Formats; @@ -13,6 +14,7 @@ import io.javalin.plugin.openapi.annotations.OpenApiContent; import io.javalin.plugin.openapi.annotations.OpenApiParam; import io.javalin.plugin.openapi.annotations.OpenApiRequestBody; +import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; @@ -45,40 +47,10 @@ private Timer.Context markAndTime(String subject) { return Controllers.markAndTime(metrics, getClass().getName(), subject); } - @OpenApi( - description = "Used to create and save a forecast timeseries", - requestBody = @OpenApiRequestBody( - content = { - @OpenApiContent(from = TimeSeries.class, type = Formats.JSONV2) - }, - required = true - ), - queryParams = { - @OpenApiParam(name = FORECAST_DATE, required = true, description = "Specifies the " + - "forecast date time of the forecast instance to be associated with the created" + - "forecast timeseries."), - @OpenApiParam(name = ISSUE_DATE, required = true, description = "Specifies the " + - "issue date time of the forecast instance to be associated with the created " + - "forecast timeseries."), - @OpenApiParam(name = OFFICE, required = true, description = "Specifies the " + - "owning office of the forecast spec whose forecast instance will be " + - "associated with the created forecast timeseries."), - @OpenApiParam(name = NAME, required = true, description = "Specifies the " + - "spec id of the forecast spec whose forecast instance will be " + - "associated with the created forecast timeseries."), - @OpenApiParam(name = LOCATION_ID, required = true, description = "Specifies the " + - "location of the forecast spec whose forecast instance will be" + - "associated with the created forecast timeseries."), - @OpenApiParam(name = TIMESERIES_ID, required = true, description = "Id of timeseries " + - "that will be created.") - }, - method = HttpMethod.POST, - path = "/forecast-timeseries", - tags = TAG - ) + @OpenApi(ignore = true) @Override public void create(@NotNull Context ctx) { - + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } protected DSLContext getDslContext(Context ctx) { @@ -88,34 +60,25 @@ protected DSLContext getDslContext(Context ctx) { @OpenApi(ignore = true) @Override public void delete(@NotNull Context ctx, @NotNull String forecastSpecId) { - try (final Timer.Context ignored = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); - } + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void getAll(@NotNull Context ctx) { - try (final Timer.Context ignored = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); - } + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void getOne(@NotNull Context ctx, @NotNull String id) { - try (final Timer.Context ignored = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); - } + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void update(@NotNull Context ctx, @NotNull String id) { - try (final Timer.Context ignored = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); - } - + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/LevelsAsTimeSeriesController.java b/cwms-data-api/src/main/java/cwms/cda/api/LevelsAsTimeSeriesController.java index 9927c069c..3b2537ae9 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/LevelsAsTimeSeriesController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/LevelsAsTimeSeriesController.java @@ -50,15 +50,10 @@ import static cwms.cda.api.Controllers.*; import static cwms.cda.data.dao.JooqDao.getDslContext; -public class LevelsAsTimeSeriesController implements Handler { - private final MetricRegistry metrics; +public class LevelsAsTimeSeriesController extends BaseHandler { public LevelsAsTimeSeriesController(MetricRegistry metrics) { - this.metrics = metrics; - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } @OpenApi( @@ -113,7 +108,7 @@ private Timer.Context markAndTime(String subject) { tags = LevelsController.TAG ) public void handle(Context ctx) { - + logUnusedPathParameter(ctx, LEVEL_ID, "Body contains required information"); try (final Timer.Context timeContext = markAndTime("getLevelAsTimeSeries")) { DSLContext dsl = getDslContext(ctx); Validator pathParam = ctx.pathParamAsClass(LEVEL_ID, String.class); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/LevelsController.java b/cwms-data-api/src/main/java/cwms/cda/api/LevelsController.java index c1dfa0de3..5ca14bfa4 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/LevelsController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/LevelsController.java @@ -161,6 +161,9 @@ private LocationLevel deserializeLocationLevel(Context ctx) throws IOException { @OpenApiParam(name = EFFECTIVE_DATE, description = "Specifies the " + "effective date of the level to be deleted. If not provided will " + "delete all data and reference to the location level."), + @OpenApiParam(name = DATE, deprecated = true, description = "Specifies " + + "the effective date of Location Level that will be deleted." + + " Deprecated, use " + EFFECTIVE_DATE + " instead"), @OpenApiParam(name = TIMEZONE, description = "Specifies the time zone of " + "the value of the effective date field (unless otherwise " + "specified).If this field is not specified, the default time zone of UTC " @@ -196,6 +199,9 @@ public void delete(@NotNull Context ctx, @NotNull String levelId) { @OpenApiParam(name = LEVEL_ID_MASK, description = "Specifies the name(s) of " + "the location level(s) whose data is to be included in the response. " + "Uses * for all."), + @OpenApiParam(name = NAME, deprecated = true, description = "Specifies the name(s) of " + + "the location level(s) whose data is to be included in the response. " + + "Uses * for all. Deprecated, use " + LEVEL_ID_MASK + " instead"), @OpenApiParam(name = OFFICE, description = "Specifies the owning " + "office of the location level(s) whose data is to be included in the" + " response. If this field is not specified, matching location level " @@ -338,6 +344,9 @@ public void getAll(@NotNull Context ctx) { @OpenApiParam(name = EFFECTIVE_DATE, required = true, description = "Specifies " + "the effective date of Location Level to be returned." + "Expected formats are `YYYY-MM-DDTHH:MM` or `YYYY-MM-DDTHH:MM:SS`"), + @OpenApiParam(name = DATE, deprecated = true, description = "Specifies " + + "the effective date of Location Level that will be returned." + + " Deprecated, use " + EFFECTIVE_DATE + " instead"), @OpenApiParam(name = EFFECTIVE_DATE_EXACT, description = "If true" + " only a level with the exact provided date will be returned. If false" + " The most recent level on or before this time will be returned." @@ -401,7 +410,10 @@ String.class, null, metrics, name(LevelsController.class.getName(), }, queryParams = { @OpenApiParam(name = EFFECTIVE_DATE, description = "Specifies " - + "the effective date of Location Level that will be updated") + + "the effective date of Location Level that will be updated"), + @OpenApiParam(name = DATE, deprecated = true, description = "Specifies " + + "the effective date of Location Level that will be updated." + + " Deprecated, use " + EFFECTIVE_DATE + " instead") }, requestBody = @OpenApiRequestBody( content = { diff --git a/cwms-data-api/src/main/java/cwms/cda/api/LocationController.java b/cwms-data-api/src/main/java/cwms/cda/api/LocationController.java index 791379f34..9bce96e1d 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/LocationController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/LocationController.java @@ -205,6 +205,9 @@ public void getAll(@NotNull Context ctx) { } @OpenApi( + pathParams = { + @OpenApiParam(name = LOCATION_ID, description = "The ID of the location to get") + }, queryParams = { @OpenApiParam(name = OFFICE, required = true, description = "Specifies the " + "owning office of the location level(s) whose data is to be " @@ -232,7 +235,7 @@ public void getAll(@NotNull Context ctx) { tags = {"Locations"} ) @Override - public void getOne(@NotNull Context ctx, @NotNull String name) { + public void getOne(@NotNull Context ctx, @NotNull String locationId) { try (final Timer.Context ignored = markAndTime(GET_ONE)) { DSLContext dsl = getDslContext(ctx); @@ -246,12 +249,12 @@ public void getOne(@NotNull Context ctx, @NotNull String name) { ContentType contentType = Formats.parseHeader(formatHeader, Location.class); ctx.contentType(contentType.toString()); LocationsDao locationDao = getLocationsDao(dsl); - Location location = locationDao.getLocation(name, units, office, includeAliases); + Location location = locationDao.getLocation(locationId, units, office, includeAliases); String serializedLocation = Formats.format(contentType, location); ctx.result(serializedLocation); addDeprecatedContentTypeWarning(ctx, contentType); } catch (IOException ex) { - String errorMsg = "Error retrieving " + name; + String errorMsg = "Error retrieving " + locationId; CdaError re = new CdaError(errorMsg); ctx.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).json(re); logger.atSevere().withCause(ex).log("%s", errorMsg); @@ -301,6 +304,9 @@ public void create(@NotNull Context ctx) { } @OpenApi( + pathParams = { + @OpenApiParam(name = LOCATION_ID, description = "The ID of the location to update") + }, requestBody = @OpenApiRequestBody( content = { @OpenApiContent(from = Location.class, type = Formats.XML), @@ -354,15 +360,16 @@ public void update(@NotNull Context ctx, @NotNull String locationId) { } @OpenApi( + pathParams = { + @OpenApiParam(name = LOCATION_ID, description = "The ID of the location to delete") + }, queryParams = { @OpenApiParam(name = OFFICE, description = "Specifies the owning office of " + "the location whose data is to be deleted. If this field is not " + "specified, matching location information will be deleted from all " + "offices."), - //Keeping hidden from the API docs for now as this call is particularly destructive - //@OpenApiParam(name = CASCADE_DELETE, type = Boolean.class, - //description = "Specifies whether to specifies whether to delete associated data " + - //"for this location before deleting the location itself. Default: false") + @OpenApiParam(name = CASCADE_DELETE, type = Boolean.class, description = "Specifies whether to delete" + + " associated data for this location before deleting the location itself. Default: false") }, description = "Delete CWMS Location", method = HttpMethod.DELETE, diff --git a/cwms-data-api/src/main/java/cwms/cda/api/LocationGroupController.java b/cwms-data-api/src/main/java/cwms/cda/api/LocationGroupController.java index 48c858a3f..31f2d5815 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/LocationGroupController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/LocationGroupController.java @@ -263,6 +263,10 @@ public void create(@NotNull Context ctx) { @OpenApiContent(from = LocationGroup.class, type = Formats.JSON) }, required = true), + pathParams = { + @OpenApiParam(name = GROUP_ID, required = true, description = "Specifies " + + "the location_group to be renamed.") + }, queryParams = { @OpenApiParam(name = REPLACE_ASSIGNED_LOCS, type = Boolean.class, description = "Specifies whether to " + "unassign all existing locations before assigning new locations specified in the content body " @@ -276,7 +280,7 @@ public void create(@NotNull Context ctx) { tags = {TAG} ) @Override - public void update(@NotNull Context ctx, @NotNull String oldGroupId) { + public void update(@NotNull Context ctx, @NotNull String groupId) { try (Timer.Context ignored = markAndTime(CREATE)) { DSLContext dsl = getDslContext(ctx); @@ -288,8 +292,8 @@ public void update(@NotNull Context ctx, @NotNull String oldGroupId) { boolean replaceAssignedLocs = ctx.queryParamAsClass(REPLACE_ASSIGNED_LOCS, Boolean.class).getOrDefault(false); LocationGroupDao locationGroupDao = new LocationGroupDao(dsl); - if (!office.equalsIgnoreCase(CWMS_OFFICE) && !oldGroupId.equals(deserialize.getId())) { - locationGroupDao.renameLocationGroup(oldGroupId, deserialize); + if (!office.equalsIgnoreCase(CWMS_OFFICE) && !groupId.equals(deserialize.getId())) { + locationGroupDao.renameLocationGroup(groupId, deserialize); } if (replaceAssignedLocs) { locationGroupDao.unassignAllLocs(deserialize, office); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/LocationKindController.java b/cwms-data-api/src/main/java/cwms/cda/api/LocationKindController.java index f61bf96f3..2b5bc56a4 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/LocationKindController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/LocationKindController.java @@ -103,9 +103,8 @@ public void handle(@NotNull Context ctx) { String kindRegexMask = ctx.queryParam(LOCATION_KIND_LIKE); String office = ctx.queryParam(OFFICE); - String formatParm = ctx.queryParamAsClass(Formats.JSONV1, String.class).getOrDefault(""); String formatHeader = ctx.header(Header.ACCEPT); - ContentType contentType = Formats.parseHeaderAndQueryParm(formatHeader, formatParm, CwmsIdLocationKind.class); + ContentType contentType = Formats.parseHeader(formatHeader, CwmsIdLocationKind.class); String results; diff --git a/cwms-data-api/src/main/java/cwms/cda/api/LookupTypeController.java b/cwms-data-api/src/main/java/cwms/cda/api/LookupTypeController.java index 139039862..f6a5cc4f5 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/LookupTypeController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/LookupTypeController.java @@ -27,6 +27,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.LookupTypeDao; import cwms.cda.data.dto.LookupType; import cwms.cda.data.dto.StatusResponse; @@ -51,23 +52,12 @@ import static cwms.cda.api.Controllers.*; import static cwms.cda.data.dao.JooqDao.getDslContext; -public final class LookupTypeController implements CrudHandler { +public final class LookupTypeController extends BaseCrudHandler { static final String TAG = "LookupTypes"; - private final MetricRegistry metrics; - - private final Histogram requestResultSize; - public LookupTypeController(MetricRegistry metrics) { - this.metrics = metrics; - String className = this.getClass().getName(); - - requestResultSize = this.metrics.histogram((name(className, RESULTS, SIZE))); - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } @OpenApi( @@ -99,16 +89,14 @@ public void getAll(Context ctx) { String serialized = Formats.format(contentType, lookupTypes, LookupType.class); ctx.result(serialized); ctx.status(HttpServletResponse.SC_OK); - requestResultSize.update(serialized.length()); + updateResultSize(serialized.length()); } } @OpenApi(ignore = true) @Override public void getOne(@NotNull Context context, @NotNull String s) { - try (final Timer.Context ignored = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); - } + context.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( @@ -146,6 +134,9 @@ public void create(Context ctx) { } @OpenApi( + pathParams = { + @OpenApiParam(name = NAME, required = true, description = "Specifies the location type to update.") + }, queryParams = { @OpenApiParam(name = CATEGORY, required = true, description = "Specifies the category id of the lookup type to be updated."), @OpenApiParam(name = PREFIX, required = true, description = "Specifies the prefix of the lookup type to be updated."), @@ -164,6 +155,7 @@ public void create(Context ctx) { ) @Override public void update(Context ctx, String name) { + logUnusedPathParameter(ctx, NAME, "Body has required information"); String category = requiredParam(ctx, CATEGORY); String prefix = requiredParam(ctx, PREFIX); try (Timer.Context ignored = markAndTime(UPDATE)) { @@ -180,6 +172,9 @@ public void update(Context ctx, String name) { } @OpenApi( + pathParams = { + @OpenApiParam(name = NAME, required = true, description = "Specifies the location type to delete.") + }, queryParams = { @OpenApiParam(name = CATEGORY, required = true, description = "Specifies the category id of the lookup type to be deleted."), @OpenApiParam(name = PREFIX, required = true, description = "Specifies the prefix of the lookup type to be deleted."), diff --git a/cwms-data-api/src/main/java/cwms/cda/api/MeasurementController.java b/cwms-data-api/src/main/java/cwms/cda/api/MeasurementController.java index 9c3cc445f..be7ea5b71 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/MeasurementController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/MeasurementController.java @@ -56,6 +56,7 @@ import static cwms.cda.api.Controllers.queryParamAsInstant; import static cwms.cda.api.Controllers.requiredParam; import cwms.cda.api.enums.UnitSystem; +import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.MeasurementDao; import cwms.cda.data.dto.StatusResponse; import cwms.cda.data.dto.measurement.Measurement; @@ -165,10 +166,7 @@ public void getAll(@NotNull Context ctx) { @OpenApi(ignore = true) @Override public void getOne(@NotNull Context ctx, @NotNull String locationId) { - try (final Timer.Context ignored = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); - } - + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( @@ -208,9 +206,7 @@ public void create(Context ctx) { @OpenApi(ignore = true) @Override public void update(@NotNull Context ctx, @NotNull String locationId) { - try (final Timer.Context ignored = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); - } + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ParametersController.java b/cwms-data-api/src/main/java/cwms/cda/api/ParametersController.java index dcd9f0918..d2791de33 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ParametersController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ParametersController.java @@ -52,13 +52,13 @@ private Timer.Context markAndTime(String subject) { @OpenApi(ignore = true) @Override public void create(Context ctx) { - ctx.status(HttpServletResponse.SC_NOT_FOUND); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void delete(Context ctx, String id) { - ctx.status(HttpServletResponse.SC_NOT_FOUND); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @@ -131,16 +131,13 @@ public void getAll(Context ctx) { @OpenApi(ignore = true) @Override public void getOne(Context ctx, String id) { - try (final Timer.Context timeContext = markAndTime(GET_ONE)) { - ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); - } + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void update(Context ctx, String id) { ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); - } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/PoolController.java b/cwms-data-api/src/main/java/cwms/cda/api/PoolController.java index 5d7616031..2841cdc2b 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/PoolController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/PoolController.java @@ -83,6 +83,11 @@ private Timer.Context markAndTime(String subject) { + " in the request you are. This is an opaque value, and can be" + " obtained from the 'next-page' value in the response." ), + @OpenApiParam(name = CURSOR, deprecated = true, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response. Deprecated, use " + PAGE + " instead."), @OpenApiParam(name = PAGE_SIZE, type = Integer.class, description = @@ -226,18 +231,18 @@ public void getOne(@NotNull Context ctx, @NotNull String poolId) { @OpenApi(ignore = true) @Override public void create(@NotNull Context ctx) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void update(@NotNull Context ctx, @NotNull String locationCode) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void delete(@NotNull Context ctx, @NotNull String locationCode) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/PropertyController.java b/cwms-data-api/src/main/java/cwms/cda/api/PropertyController.java index 7a2459a1e..78707d30a 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/PropertyController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/PropertyController.java @@ -52,31 +52,18 @@ import static cwms.cda.api.Controllers.*; import static cwms.cda.data.dao.JooqDao.getDslContext; -public final class PropertyController implements CrudHandler { +public final class PropertyController extends BaseCrudHandler { static final String TAG = "Properties"; - private final MetricRegistry metrics; - - private final Histogram requestResultSize; - public PropertyController(MetricRegistry metrics) { - this.metrics = metrics; - String className = this.getClass().getName(); - - requestResultSize = this.metrics.histogram((name(className, RESULTS, SIZE))); - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } @OpenApi( - pathParams = { - }, queryParams = { @OpenApiParam(name = OFFICE_MASK, description = "Filters properties to the specified office mask"), - @OpenApiParam(name = CATEGORY_ID, description = "Filters properties to the specified category mask"), + @OpenApiParam(name = CATEGORY_ID_MASK, description = "Filters properties to the specified category mask"), @OpenApiParam(name = NAME_MASK, description = "Filters properties to the specified name mask"), }, responses = { @@ -105,7 +92,7 @@ public void getAll(Context ctx) { String serialized = Formats.format(contentType, properties, Property.class); ctx.result(serialized); ctx.status(HttpServletResponse.SC_OK); - requestResultSize.update(serialized.length()); + updateResultSize(serialized.length()); } } @@ -149,7 +136,7 @@ public void getOne(Context ctx, String name) { String serialized = Formats.format(contentType, property); ctx.result(serialized); ctx.status(HttpServletResponse.SC_OK); - requestResultSize.update(serialized.length()); + updateResultSize(serialized.length()); } } @@ -184,6 +171,10 @@ public void create(Context ctx) { } @OpenApi( + pathParams = { + @OpenApiParam(name = NAME, required = true, description = "Specifies the name of the property to be " + + "updated."), + }, requestBody = @OpenApiRequestBody( content = { @OpenApiContent(from = Property.class, type = Formats.JSON) @@ -198,6 +189,7 @@ public void create(Context ctx) { ) @Override public void update(Context ctx, String name) { + logUnusedPathParameter(ctx, NAME, "Body contains required information"); try (Timer.Context ignored = markAndTime(UPDATE)) { String formatHeader = ctx.req.getContentType(); ContentType contentType = Formats.parseHeader(formatHeader, Property.class); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/SpecifiedLevelController.java b/cwms-data-api/src/main/java/cwms/cda/api/SpecifiedLevelController.java index 1686f6e63..550c9efda 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/SpecifiedLevelController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/SpecifiedLevelController.java @@ -122,8 +122,7 @@ public void getAll(Context ctx) { @OpenApi(ignore = true) @Override public void getOne(Context ctx, String templateId) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); //To change body of - // generated methods, choose Tools | Specs. + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/StandardTextController.java b/cwms-data-api/src/main/java/cwms/cda/api/StandardTextController.java index 79e06dadf..0038a39f7 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/StandardTextController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/StandardTextController.java @@ -26,6 +26,7 @@ import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.DeleteRule; import cwms.cda.data.dao.JooqDao; import cwms.cda.data.dao.texttimeseries.StandardTextDao; @@ -76,7 +77,9 @@ private Timer.Context markAndTime(String subject) { @OpenApiParam(name = OFFICE_MASK, description = "Specifies the office filter of the" + "standard text."), @OpenApiParam(name = STANDARD_TEXT_ID_MASK, description = "Specifies the text id filter of the " - + "standard text") + + "standard text"), + @OpenApiParam(name = NAME_MASK, deprecated = true, description = "Specifies the text id filter of the " + + "standard text. Deprecated, use " + STANDARD_TEXT_ID_MASK) }, responses = { @OpenApiResponse(status = STATUS_200, @@ -94,10 +97,7 @@ public void getAll(Context ctx) { if (officeMask == null) { officeMask = "*"; } - String idMask = ctx.queryParam(NAME_MASK); - if (idMask == null) { - idMask = "*"; - } + String idMask = queryParamAsClass(ctx, new String[]{STANDARD_TEXT_ID_MASK, NAME_MASK}, String.class, "*"); String formatHeader = ctx.header(Header.ACCEPT); ContentType contentType = Formats.parseHeader(formatHeader, StandardTextCatalog.class); DSLContext dsl = getDslContext(ctx); @@ -182,7 +182,7 @@ public void create(@NotNull Context ctx) { @OpenApi(ignore = true) @Override public void update(@NotNull Context ctx, @NotNull String oldTextTimeSeriesId) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/StreamLocationController.java b/cwms-data-api/src/main/java/cwms/cda/api/StreamLocationController.java index 3e3f4813c..8c3f84ad9 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/StreamLocationController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/StreamLocationController.java @@ -71,21 +71,12 @@ import static cwms.cda.data.dao.JooqDao.getDslContext; -public final class StreamLocationController implements CrudHandler { +public final class StreamLocationController extends BaseCrudHandler { static final String TAG = "StreamLocations"; - private final MetricRegistry metrics; - private final Histogram requestResultSize; - public StreamLocationController(MetricRegistry metrics) { - this.metrics = metrics; - String className = this.getClass().getName(); - requestResultSize = this.metrics.histogram((name(className, RESULTS, SIZE))); - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } @OpenApi( @@ -128,7 +119,7 @@ public void getAll(Context ctx) { String serialized = Formats.format(contentType, streamLocations, StreamLocation.class); ctx.result(serialized); ctx.status(HttpServletResponse.SC_OK); - requestResultSize.update(serialized.length()); + updateResultSize(serialized.length()); } } @@ -174,7 +165,7 @@ public void getOne(@NotNull Context ctx, @NotNull String locationId) { String serialized = Formats.format(contentType, streamLocation); ctx.result(serialized); ctx.status(HttpServletResponse.SC_OK); - requestResultSize.update(serialized.length()); + updateResultSize(serialized.length()); } } @@ -212,6 +203,10 @@ public void create(Context ctx) { } @OpenApi( + pathParams = { + @OpenApiParam(name = NAME, required = true, description = "Specifies the location-id of " + + "the stream location to be renamed."), + }, requestBody = @OpenApiRequestBody( content = { @OpenApiContent(from = StreamLocation.class, type = Formats.JSONV1) @@ -225,7 +220,8 @@ public void create(Context ctx) { } ) @Override - public void update(Context ctx, @NotNull String locationId) { + public void update(Context ctx, @NotNull String name) { + logUnusedPathParameter(ctx, NAME, "Body contains required information"); try (Timer.Context ignored = markAndTime(METHOD + "update")) { String formatHeader = ctx.req.getContentType(); ContentType contentType = Formats.parseHeader(formatHeader, StreamLocation.class); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TextTimeSeriesController.java b/cwms-data-api/src/main/java/cwms/cda/api/TextTimeSeriesController.java index 4e557631e..43893dd3a 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TextTimeSeriesController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TextTimeSeriesController.java @@ -56,7 +56,7 @@ -public class TextTimeSeriesController implements CrudHandler { +public class TextTimeSeriesController extends BaseCrudHandler { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); static final String TAG = "Text-TimeSeries"; @@ -65,10 +65,8 @@ public class TextTimeSeriesController implements CrudHandler { public static final boolean DEFAULT_CREATE_REPLACE_ALL = false; public static final boolean DEFAULT_UPDATE_REPLACE_ALL = true; - private final MetricRegistry metrics; - public TextTimeSeriesController(MetricRegistry metrics) { - this.metrics = metrics; + super(metrics); } @NotNull @@ -76,12 +74,6 @@ protected TimeSeriesTextDao getDao(DSLContext dsl) { return new TimeSeriesTextDao(dsl); } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); - } - - @OpenApi( summary = "Retrieve text time series values for a provided time window and date version." + "If individual values exceed 64 kilobytes, a URL to a separate download is provided " @@ -95,6 +87,8 @@ private Timer.Context markAndTime(String subject) { + "the time zone of the values of the begin and end fields (unless " + "otherwise specified). If this field is not specified, " + "the default time zone of UTC shall be used."), + @OpenApiParam(name = VERSION_DATE, description = "Specifies the version date of the " + + "text timeseries. If not specified, the latest version will be used."), @OpenApiParam(name = BEGIN, required = true, description = "The start of the time window"), @OpenApiParam(name = END, required = true, description = "The end of the time window.") }, @@ -162,7 +156,7 @@ public void getAll(@NotNull Context ctx) { @OpenApi(ignore = true) @Override public void getOne(@NotNull Context ctx, @NotNull String templateId) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( @@ -217,7 +211,7 @@ public void create(@NotNull Context ctx) { ) @Override public void update(@NotNull Context ctx, @NotNull String oldTextTimeSeriesId) { - + logUnusedPathParameter(ctx, NAME, "Body contains required information"); try (Timer.Context ignored = markAndTime(UPDATE)) { boolean replaceAll = ctx.queryParamAsClass(REPLACE_ALL, Boolean.class) .getOrDefault(DEFAULT_UPDATE_REPLACE_ALL); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TextTimeSeriesValueController.java b/cwms-data-api/src/main/java/cwms/cda/api/TextTimeSeriesValueController.java index 7702b4006..5203b105b 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TextTimeSeriesValueController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TextTimeSeriesValueController.java @@ -43,18 +43,11 @@ import static cwms.cda.data.dao.JooqDao.getDslContext; -public class TextTimeSeriesValueController implements Handler { +public class TextTimeSeriesValueController extends BaseHandler { public static final String TEXT_PLAIN = "text/plain"; - private final MetricRegistry metrics; - private final Histogram requestResultSize; public TextTimeSeriesValueController(MetricRegistry metrics) { - this.metrics = metrics; - requestResultSize = this.metrics.histogram((name(TextTimeSeriesValueController.class, RESULTS, SIZE))); - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } @OpenApi( @@ -65,12 +58,6 @@ private Timer.Context markAndTime(String subject) { queryParams = { @OpenApiParam(name = OFFICE, required = true, description = "Specifies the owning office of " + "the Text TimeSeries whose data is 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). If this field is not specified, " - + "the default time zone of UTC shall be used."), - @OpenApiParam(name = DATE, required = true, description = "The date of the text value to retrieve"), - @OpenApiParam(name = VERSION_DATE, description = "The version date for the value to retrieve."), @OpenApiParam(name = CLOB_ID, description = "Will be removed in a schema update. " + "This is a placeholder for integration testing with schema 23.3.16", deprecated = true) }, @@ -84,7 +71,8 @@ private Timer.Context markAndTime(String subject) { ) public void handle(Context ctx) { //Implementation will change with new CWMS schema - //https://www.hec.usace.army.mil/confluence/display/CWMS/2024-02-29+Task2A+Text-ts+and+Binary-ts+Design + //https://www.hec.usace.army.mil/confluence/spaces/CWMS/pages/183110112/2024-02-29+Developer+Meeting+Task2A+Text-ts+and+Binary-ts+Design + logUnusedPathParameter(ctx, NAME, "Handled as " + CLOB_ID + " in query parameter. May change with schema."); String textId = requiredParam(ctx, CLOB_ID); String officeId = requiredParam(ctx, OFFICE); try (Timer.Context ignored = markAndTime(GET_ALL)) { @@ -92,7 +80,7 @@ public void handle(Context ctx) { ClobDao clobDao = new ClobDao(dsl); StreamConsumer consumer = (is, isPosition, mediaType, totalLength) -> { - requestResultSize.update(totalLength); + updateResultSize(totalLength); ctx.header(Header.ACCEPT_RANGES, "bytes"); RangeRequestUtil.seekableStream(ctx, is, isPosition, mediaType, totalLength); }; diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesCategoryController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesCategoryController.java index e7f455ae7..4d73ac51b 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesCategoryController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesCategoryController.java @@ -193,7 +193,7 @@ public void create(Context ctx) { @OpenApi(ignore = true) @Override public void update(@NotNull Context ctx, @NotNull String locationCode) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesController.java index 45e463879..6a7841ff2 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesController.java @@ -178,11 +178,6 @@ private Timer.Context markAndTime(String subject) { required = true ), queryParams = { - @OpenApiParam(name = TIMEZONE, description = "Specifies " - + "the time zone of the version-date field (unless " - + "otherwise specified). If this field is not specified, the default time zone " - + "of UTC shall be used.\r\nIgnored if version-date was specified with " - + "offset and timezone."), @OpenApiParam(name = CREATE_AS_LRTS, type = Boolean.class, description = "Flag indicating if " + "timeseries should be created as Local Regular Time Series. " + "'True' or 'False', default is 'False'"), @@ -417,6 +412,11 @@ public void delete(@NotNull Context ctx, @NotNull String timeseries) { + "of data as a series of pages. This parameter is used to describes the " + "current location in the response stream. This is an opaque " + "value, and can be obtained from the 'next-page' value in the response."), + @OpenApiParam(name = CURSOR, deprecated = true, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response. Deprecated, use " + PAGE + " instead."), @OpenApiParam(name = PAGE_SIZE, type = Integer.class, description = "How many entries per page returned. " @@ -580,7 +580,7 @@ private void addLinkHeader(@NotNull Context ctx, TimeSeries ts, ContentType cont public void getOne(@NotNull Context ctx, @NotNull String id) { try (final Timer.Context ignored = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } } @@ -597,11 +597,6 @@ public void getOne(@NotNull Context ctx, @NotNull String id) { }, required = true), queryParams = { - @OpenApiParam(name = TIMEZONE, description = "Specifies " - + "the time zone of the version-date field (unless " - + "otherwise specified). If this field is not specified, the default time zone " - + "of UTC shall be used.\r\nIgnored if version-date was specified with " - + "offset and timezone."), @OpenApiParam(name = CREATE_AS_LRTS, type = Boolean.class, description = ""), @OpenApiParam(name = STORE_RULE, type = StoreRule.class, description = STORE_RULE_DESC), @OpenApiParam(name = OVERRIDE_PROTECTION, type = Boolean.class, description = diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesFilteredController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesFilteredController.java index 48d339ca8..da8f26eef 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesFilteredController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesFilteredController.java @@ -141,6 +141,11 @@ private TimeSeriesDao getTimeSeriesDao(DSLContext dsl) { + "of data as a series of pages. This parameter is used to describes the " + "current location in the response stream. This is an opaque " + "value, and can be obtained from the 'next-page' value in the response."), + @OpenApiParam(name = CURSOR, deprecated = true, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response. Deprecated, use " + PAGE + " instead."), @OpenApiParam(name = PAGE_SIZE, type = Integer.class, description = "How many entries per page returned. " diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java index 9152953f6..708629088 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java @@ -97,6 +97,8 @@ private Timer.Context markAndTime(String subject) { @OpenApiParam(name = OFFICE, description = "Specifies the owning office of the " + "timeseries assigned to the group(s) whose data is to be included in the response. If this " + "field is not specified, group information for all assigned TS offices shall be returned."), + @OpenApiParam(name = GROUP_OFFICE_ID, description = "Specifies the owning office of the " + + "timeseries group", required = true), @OpenApiParam(name = INCLUDE_ASSIGNED, type = Boolean.class, description = "Include" + " the assigned timeseries in the returned timeseries groups. (default: true)"), @OpenApiParam(name = TIMESERIES_CATEGORY_LIKE, description = "Posix regular expression " @@ -276,6 +278,10 @@ public void create(@NotNull Context ctx) { @OpenApiContent(from = TimeSeriesGroup.class, type = Formats.JSON) }, required = true), + pathParams = { + @OpenApiParam(name = GROUP_ID, required = true, description = "Specifies " + + "the original timeseries group to rename.") + }, queryParams = { @OpenApiParam(name = REPLACE_ASSIGNED_TS, type = Boolean.class, description = "Specifies whether to " + "unassign all existing time series before assigning new time series specified in the content body " diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeZoneController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeZoneController.java index bdbdecc42..44cf6d9ff 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeZoneController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeZoneController.java @@ -17,6 +17,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.TimeZoneDao; import cwms.cda.data.dto.TimeZoneId; import cwms.cda.data.dto.TimeZoneIds; @@ -54,13 +55,13 @@ private Timer.Context markAndTime(String subject) { @OpenApi(ignore = true) @Override public void create(Context ctx) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void delete(Context ctx, String id) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( @@ -130,15 +131,13 @@ public void getAll(Context ctx) { @OpenApi(ignore = true) @Override public void getOne(Context ctx, String id) { - try (Timer.Context timeContext = markAndTime(GET_ONE)) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); - } + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void update(Context ctx, String id) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesDeleteController.java b/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesDeleteController.java index 6fd640da3..597a0ad4f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesDeleteController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesDeleteController.java @@ -25,25 +25,8 @@ package cwms.cda.api; import static com.codahale.metrics.MetricRegistry.name; -import static cwms.cda.api.Controllers.BEGIN; -import static cwms.cda.api.Controllers.DELETE; -import static cwms.cda.api.Controllers.END; -import static cwms.cda.api.Controllers.END_TIME_INCLUSIVE; -import static cwms.cda.api.Controllers.GET_ALL; -import static cwms.cda.api.Controllers.NAME; -import static cwms.cda.api.Controllers.OFFICE; -import static cwms.cda.api.Controllers.OVERRIDE_PROTECTION; -import static cwms.cda.api.Controllers.PAGE_SIZE; -import static cwms.cda.api.Controllers.PROJECT_ID; -import static cwms.cda.api.Controllers.RESULTS; -import static cwms.cda.api.Controllers.SIZE; -import static cwms.cda.api.Controllers.START_TIME_INCLUSIVE; -import static cwms.cda.api.Controllers.STATUS_200; -import static cwms.cda.api.Controllers.STATUS_204; -import static cwms.cda.api.Controllers.STATUS_404; -import static cwms.cda.api.Controllers.UNIT_SYSTEM; -import static cwms.cda.api.Controllers.requiredInstant; -import static cwms.cda.api.Controllers.requiredParam; +import static cwms.cda.api.Controllers.*; +import static cwms.cda.api.Controllers.SINCE; import static cwms.cda.data.dao.JooqDao.getDslContext; import com.codahale.metrics.Histogram; @@ -96,6 +79,11 @@ private Timer.Context markAndTime(String subject) { "turbine changes to be deleted."), }, queryParams = { + @OpenApiParam(name = TIMEZONE, description = "Specifies " + + "the time zone of the values of " + BEGIN + ", " + END + " fields (unless " + + "otherwise specified). If this field is not specified, the default time zone " + + "of UTC shall be used.\r\nIgnored if " + BEGIN + " was specified with " + + "offset and timezone."), @OpenApiParam(name = BEGIN, required = true, description = "The start of the time window"), @OpenApiParam(name = END, required = true, description = "The end of the time window."), @OpenApiParam(name = OVERRIDE_PROTECTION, type = Boolean.class, description = "A flag " diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesGetController.java b/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesGetController.java index d9d5a9837..e8d3b936d 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesGetController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesGetController.java @@ -25,26 +25,7 @@ package cwms.cda.api; import static com.codahale.metrics.MetricRegistry.name; -import static cwms.cda.api.Controllers.BEGIN; -import static cwms.cda.api.Controllers.CREATE; -import static cwms.cda.api.Controllers.DELETE; -import static cwms.cda.api.Controllers.END; -import static cwms.cda.api.Controllers.END_TIME_INCLUSIVE; -import static cwms.cda.api.Controllers.GET_ALL; -import static cwms.cda.api.Controllers.NAME; -import static cwms.cda.api.Controllers.OFFICE; -import static cwms.cda.api.Controllers.OVERRIDE_PROTECTION; -import static cwms.cda.api.Controllers.PAGE_SIZE; -import static cwms.cda.api.Controllers.PROJECT_ID; -import static cwms.cda.api.Controllers.RESULTS; -import static cwms.cda.api.Controllers.SIZE; -import static cwms.cda.api.Controllers.START_TIME_INCLUSIVE; -import static cwms.cda.api.Controllers.STATUS_200; -import static cwms.cda.api.Controllers.STATUS_204; -import static cwms.cda.api.Controllers.STATUS_404; -import static cwms.cda.api.Controllers.UNIT_SYSTEM; -import static cwms.cda.api.Controllers.requiredInstant; -import static cwms.cda.api.Controllers.requiredParam; +import static cwms.cda.api.Controllers.*; import static cwms.cda.data.dao.JooqDao.getDslContext; import com.codahale.metrics.Histogram; @@ -100,6 +81,11 @@ private Timer.Context markAndTime(String subject) { "Turbine changes whose data is to be included in the response."), }, queryParams = { + @OpenApiParam(name = TIMEZONE, description = "Specifies " + + "the time zone of the values of " + BEGIN + ", " + END + " fields (unless " + + "otherwise specified). If this field is not specified, the default time zone " + + "of UTC shall be used.\r\nIgnored if " + BEGIN + " was specified with " + + "offset and timezone."), @OpenApiParam(name = BEGIN, required = true, description = "The start of the time window"), @OpenApiParam(name = END, required = true, description = "The end of the time window."), @OpenApiParam(name = START_TIME_INCLUSIVE, type = Boolean.class, description = "A flag " diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesPostController.java b/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesPostController.java index dda5a6dd0..533f93044 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesPostController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TurbineChangesPostController.java @@ -75,16 +75,11 @@ import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; -public final class TurbineChangesPostController implements Handler { - private final MetricRegistry metrics; +public final class TurbineChangesPostController extends BaseHandler { public TurbineChangesPostController(MetricRegistry metrics) { - this.metrics = metrics; - } - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + super(metrics); } @OpenApi( @@ -115,6 +110,9 @@ private Timer.Context markAndTime(String subject) { ) @Override public void handle(@NotNull Context ctx) throws Exception { + logUnusedPathParameter(ctx, NAME, "Body contains required information."); + logUnusedPathParameter(ctx, OFFICE, "Body contains required information."); + try (Timer.Context ignored = markAndTime(CREATE)) { String formatHeader = ctx.req.getContentType(); ContentType contentType = Formats.parseHeader(formatHeader, TurbineChange.class); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/auth/ApiKeyController.java b/cwms-data-api/src/main/java/cwms/cda/api/auth/ApiKeyController.java index 279c0843a..59329d573 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/auth/ApiKeyController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/auth/ApiKeyController.java @@ -46,6 +46,7 @@ import io.javalin.plugin.openapi.annotations.OpenApiSecurity; import java.util.List; +import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; @@ -97,6 +98,10 @@ public void create(Context ctx) { } @OpenApi( + pathParams = { + @OpenApiParam(name = "key-name", required = true, + description = "Name of the specific key to get more information for. NOTE: Case-sensitive.") + }, responses = @OpenApiResponse( content = { @OpenApiContent(from = ApiKey.class, type = Formats.JSON) @@ -180,7 +185,7 @@ public void getOne(Context ctx, @NotNull String keyName) { ) @Override public void update(@NotNull Context ctx, @NotNull String arg1) { - throw new UnsupportedOperationException("Update is not implemented. Delete and create a new key."); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/auth/users/UsersController.java b/cwms-data-api/src/main/java/cwms/cda/api/auth/users/UsersController.java index e23602162..aa08a202e 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/auth/users/UsersController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/auth/users/UsersController.java @@ -1,21 +1,12 @@ package cwms.cda.api.auth.users; import static com.codahale.metrics.MetricRegistry.name; -import static cwms.cda.api.Controllers.CURSOR; -import static cwms.cda.api.Controllers.GET_ALL; -import static cwms.cda.api.Controllers.INCLUDE_VALUES; -import static cwms.cda.api.Controllers.OFFICE; -import static cwms.cda.api.Controllers.PAGE; -import static cwms.cda.api.Controllers.PAGE_SIZE; -import static cwms.cda.api.Controllers.STATUS_200; -import static cwms.cda.api.Controllers.STATUS_201; -import static cwms.cda.api.Controllers.STATUS_204; -import static cwms.cda.api.Controllers.markAndTime; -import static cwms.cda.api.Controllers.queryParamAsClass; +import static cwms.cda.api.Controllers.*; import static cwms.cda.data.dao.JooqDao.getDslContext; import java.util.List; +import javax.servlet.http.HttpServletResponse; import org.jooq.DSLContext; import com.codahale.metrics.MetricRegistry; @@ -63,14 +54,13 @@ private Timer.Context markAndTime(String subject) { @OpenApi(ignore = true) @Override public void create(Context ctx) { - throw new UnsupportedOperationException("Unimplemented method 'create'"); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void delete(Context ctx, String username) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'delete'"); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @@ -84,10 +74,19 @@ public void delete(Context ctx, String username) { + "identifies where in the request you are. This is an opaque" + " value, and can be obtained from the 'next-page' value in " + "the response."), + @OpenApiParam(name = CURSOR, deprecated = true, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response. Deprecated, use " + PAGE + " instead."), @OpenApiParam(name = PAGE_SIZE, type = Integer.class, description = "How many entries per page returned. Default " - + DEFAULT_PAGE_SIZE + ".") + + DEFAULT_PAGE_SIZE + "."), + @OpenApiParam(name = INCLUDE_ROLES, + type = Boolean.class, + allowEmptyValue = true, + description = "Include roles in the response. Default false.") }, responses = @OpenApiResponse( content = { @@ -122,7 +121,7 @@ public void getAll(Context ctx) { int pageSize = queryParamAsClass(ctx, new String[]{PAGE_SIZE}, Integer.class, DEFAULT_PAGE_SIZE, metrics, name(UsersController.class.getName(), GET_ALL)); - boolean includeRoles = queryParamAsClass(ctx, new String[]{"include-roles"}, + boolean includeRoles = queryParamAsClass(ctx, new String[]{INCLUDE_ROLES}, Boolean.class, false, metrics, name(UsersController.class.getName(), GET_ALL)); UserDao dao = new UserDao(dsl); @@ -170,9 +169,6 @@ public void getOne(Context ctx, String userName) { ) @Override public void update(Context ctx, String arg1) { - throw new UnsupportedOperationException("Unimplemented method 'update'"); + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } - - - } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/location/kind/GateChangeDeleteController.java b/cwms-data-api/src/main/java/cwms/cda/api/location/kind/GateChangeDeleteController.java index 0d27aee91..9fbf1bf82 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/location/kind/GateChangeDeleteController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/location/kind/GateChangeDeleteController.java @@ -53,6 +53,10 @@ public GateChangeDeleteController(MetricRegistry metrics) { "Gate Changes whose data is to be included in the response."), }, queryParams = { + @OpenApiParam(name = TIMEZONE, description = "This field specifies a default " + + "timezone to be used if the format of the " + + BEGIN + " and " + END + " parameters do not include " + + "offset or time zone information. Defaults to UTC."), @OpenApiParam(name = BEGIN, required = true, description = "The start of the time window"), @OpenApiParam(name = END, required = true, description = "The end of the time window."), @OpenApiParam(name = OVERRIDE_PROTECTION, type = Boolean.class, description = "A flag " diff --git a/cwms-data-api/src/main/java/cwms/cda/api/location/kind/GateChangeGetAllController.java b/cwms-data-api/src/main/java/cwms/cda/api/location/kind/GateChangeGetAllController.java index c8226be94..e3761f8f6 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/location/kind/GateChangeGetAllController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/location/kind/GateChangeGetAllController.java @@ -59,6 +59,10 @@ public GateChangeGetAllController(MetricRegistry metrics) { "Gate Changes whose data is to be included in the response."), }, queryParams = { + @OpenApiParam(name = TIMEZONE, description = "This field specifies a default " + + "timezone to be used if the format of the " + + BEGIN + " and " + END + " parameters do not include " + + "offset or time zone information. Defaults to UTC."), @OpenApiParam(name = BEGIN, required = true, description = "The start of the time window"), @OpenApiParam(name = END, required = true, description = "The end of the time window."), @OpenApiParam(name = START_TIME_INCLUSIVE, type = Boolean.class, description = "A flag " diff --git a/cwms-data-api/src/main/java/cwms/cda/api/project/ProjectLockRevoke.java b/cwms-data-api/src/main/java/cwms/cda/api/project/ProjectLockRevoke.java index 43f6eeb16..ef13e4548 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/project/ProjectLockRevoke.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/project/ProjectLockRevoke.java @@ -66,6 +66,7 @@ public ProjectLockRevoke(MetricRegistry metrics) { queryParams = { @OpenApiParam(name = OFFICE, required = true, description = "Specifies the office of the lock."), + @OpenApiParam(name = APPLICATION_ID, required = true, description = "Specifies the application id."), @OpenApiParam(name = REVOKE_TIMEOUT, type = Integer.class, description = "time in seconds to wait for existing lock to be revoked. Default: 10") }, diff --git a/cwms-data-api/src/main/java/cwms/cda/api/project/ProjectPublishStatusUpdate.java b/cwms-data-api/src/main/java/cwms/cda/api/project/ProjectPublishStatusUpdate.java index 77ebc7eed..1f94272a7 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/project/ProjectPublishStatusUpdate.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/project/ProjectPublishStatusUpdate.java @@ -24,23 +24,13 @@ package cwms.cda.api.project; -import static cwms.cda.api.Controllers.APPLICATION_ID; -import static cwms.cda.api.Controllers.BEGIN; -import static cwms.cda.api.Controllers.END; -import static cwms.cda.api.Controllers.NAME; -import static cwms.cda.api.Controllers.OFFICE; -import static cwms.cda.api.Controllers.SOURCE_ID; -import static cwms.cda.api.Controllers.STATUS_200; -import static cwms.cda.api.Controllers.TIMESERIES_ID; -import static cwms.cda.api.Controllers.queryParamAsInstant; -import static cwms.cda.api.Controllers.requiredParam; - import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import cwms.cda.api.Controllers; import cwms.cda.api.ProjectController; import cwms.cda.data.dao.JooqDao; import cwms.cda.data.dao.project.ProjectDao; +import cwms.cda.formatters.Formats; import io.javalin.http.Context; import io.javalin.http.Handler; import io.javalin.plugin.openapi.annotations.HttpMethod; @@ -50,6 +40,7 @@ import java.time.Instant; import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; +import static cwms.cda.api.Controllers.*; public class ProjectPublishStatusUpdate implements Handler { @@ -80,6 +71,13 @@ public ProjectPublishStatusUpdate(MetricRegistry metrics) { @OpenApiParam(name = TIMESERIES_ID, description = "A time series identifier of " + "the time series associated with the update. If NULL or not " + "specified, the generated message will not include this item."), + @OpenApiParam(name = TIMEZONE, description = "Specifies " + + "the time zone of the values of the begin and end fields (unless " + + "otherwise specified). For other formats this parameter " + + "affects the time zone of times in the " + + "response. If this field is not specified, the default time zone " + + "of UTC shall be used.\r\nIgnored if begin was specified with " + + "offset and timezone."), @OpenApiParam(name = BEGIN, description = "The start time of the updates to " + "the time series. If NULL or not specified, the generated message " + "will not include this item."), diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java index 24dd660c5..78d8ed7dd 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java @@ -57,6 +57,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import cwms.cda.api.BaseCrudHandler; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import cwms.cda.api.Controllers; import cwms.cda.api.errors.CdaError; @@ -101,23 +102,17 @@ -public class RatingController implements CrudHandler { +public class RatingController extends BaseCrudHandler { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); static final String TAG = "Ratings"; - private final MetricRegistry metrics; - - private final Histogram requestResultSize; - static { JavalinValidation.register(RatingSet.DatabaseLoadMethod.class, RatingController::getDatabaseLoadMethod); } public RatingController(MetricRegistry metrics) { - this.metrics = metrics; - String className = this.getClass().getName(); - requestResultSize = this.metrics.histogram((name(className, RESULTS, SIZE))); + super(metrics); } private static RatingSet.DatabaseLoadMethod getDatabaseLoadMethod(String input) { @@ -185,10 +180,6 @@ public void create(@NotNull Context ctx) { } } - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); - } - private String deserializeRatingSet(Context ctx, boolean storeTemplate) throws IOException, RatingException { String formatHeader = ctx.req.getContentType(); //Using placeholder CwmsDTOBase.class since we do not have a RatingSet DTO @@ -360,7 +351,7 @@ public void getAll(@NotNull Context ctx) { ctx.status(HttpServletResponse.SC_OK); ctx.result(results); addDeprecatedContentTypeWarning(ctx, contentType); - requestResultSize.update(results.length()); + updateResultSize(results.length()); } } @@ -566,7 +557,7 @@ private RatingSet getRatingSet(Context ctx, RatingSet.DatabaseLoadMethod method, }, method = HttpMethod.PATCH, path = "/ratings", tags = {TAG}) public void update(@NotNull Context ctx, @NotNull String ratingId) { - + logUnusedPathParameter(ctx, RATING_ID, "Body contains required information"); try (final Timer.Context ignored = markAndTime(UPDATE)) { DSLContext dsl = getDslContext(ctx); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingMetadataController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingMetadataController.java index eba0d857a..ce95a2f17 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingMetadataController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingMetadataController.java @@ -179,8 +179,7 @@ public void getAll(Context ctx) { @OpenApi(ignore = true) @Override public void getOne(Context ctx, String ratingId) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); //To change body of - // generated methods, choose Tools | Specs. + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @NotNull @@ -192,22 +191,19 @@ protected RatingMetadataDao getDao(DSLContext dsl) { @OpenApi(ignore = true) @Override public void create(Context ctx) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); //To change body of - // generated methods, choose Tools | Specs. + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void update(Context ctx, String locationCode) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); //To change body of - // generated methods, choose Tools | Specs. + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi(ignore = true) @Override public void delete(Context ctx, String locationCode) { - throw new UnsupportedOperationException(NOT_SUPPORTED_YET); //To change body of - // generated methods, choose Tools | Specs. + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java index 4b5477a6f..c963ad079 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java @@ -257,8 +257,7 @@ private static String translateJsonToXml(String body) { @OpenApi(ignore = true) @Override public void update(Context ctx, String locationCode) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of - // generated methods, choose Tools | Specs. + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java index d0cf75b7e..5b5a1e49a 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java @@ -258,8 +258,7 @@ private static String translateJsonToXml(String body) { @OpenApi(ignore = true) @Override public void update(Context ctx, String locationCode) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of - // generated methods, choose Tools | Templates. + ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rss/RssHandler.java b/cwms-data-api/src/main/java/cwms/cda/api/rss/RssHandler.java index 43d5b8f65..e33e4e052 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rss/RssHandler.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rss/RssHandler.java @@ -24,18 +24,7 @@ package cwms.cda.api.rss; -import static cwms.cda.api.Controllers.CURSOR; -import static cwms.cda.api.Controllers.GET_ALL; -import static cwms.cda.api.Controllers.NAME; -import static cwms.cda.api.Controllers.OFFICE; -import static cwms.cda.api.Controllers.PAGE; -import static cwms.cda.api.Controllers.PAGE_SIZE; -import static cwms.cda.api.Controllers.SINCE; -import static cwms.cda.api.Controllers.STATUS_200; -import static cwms.cda.api.Controllers.STATUS_400; -import static cwms.cda.api.Controllers.STATUS_404; -import static cwms.cda.api.Controllers.queryParamAsClass; -import static cwms.cda.api.Controllers.queryParamAsInstant; +import static cwms.cda.api.Controllers.*; import static cwms.cda.data.dao.JooqDao.getDslContext; import com.codahale.metrics.MetricRegistry; @@ -81,13 +70,23 @@ public RssHandler(MetricRegistry metrics) { "eg TS_STORED, STATUS, REALTIME_OPS") }, queryParams = { + @OpenApiParam(name = TIMEZONE, description = "Specifies " + + "the time zone of the values of " + SINCE + " fields (unless " + + "otherwise specified). If this field is not specified, the default time zone " + + "of UTC shall be used.\r\nIgnored if " + SINCE + " was specified with " + + "offset and timezone."), @OpenApiParam(name = SINCE, description = "The start the feed time window. " + "The endpoint will not retrieve more than the last week of messages."), @OpenApiParam(name = PAGE_SIZE, type = Integer.class, description = "The number of feed items to include."), @OpenApiParam(name = PAGE, description = "This end point can return a lot of data, this " + "identifies where in the request you are. This is an opaque" + " value, and can be obtained from the 'next-page' value in " - + "the response.") + + "the response."), + @OpenApiParam(name = CURSOR, deprecated = true, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response. Deprecated, use " + PAGE + " instead."), }, responses = { @OpenApiResponse(status = STATUS_200, content = { diff --git a/cwms-data-api/src/main/java/cwms/cda/api/timeseriesprofile/TimeSeriesProfileInstanceCreateController.java b/cwms-data-api/src/main/java/cwms/cda/api/timeseriesprofile/TimeSeriesProfileInstanceCreateController.java index b85da41c0..ccdeb5528 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/timeseriesprofile/TimeSeriesProfileInstanceCreateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/timeseriesprofile/TimeSeriesProfileInstanceCreateController.java @@ -26,14 +26,7 @@ package cwms.cda.api.timeseriesprofile; -import static cwms.cda.api.Controllers.CREATE; -import static cwms.cda.api.Controllers.METHOD; -import static cwms.cda.api.Controllers.OVERRIDE_PROTECTION; -import static cwms.cda.api.Controllers.PROFILE_DATA; -import static cwms.cda.api.Controllers.VERSION; -import static cwms.cda.api.Controllers.VERSION_DATE; -import static cwms.cda.api.Controllers.requiredInstant; -import static cwms.cda.api.Controllers.requiredParam; +import static cwms.cda.api.Controllers.*; import static cwms.cda.data.dao.JooqDao.getDslContext; import com.codahale.metrics.MetricRegistry; @@ -67,6 +60,11 @@ public TimeSeriesProfileInstanceCreateController(MetricRegistry metrics) { + " time series profile instance. Default is REPLACE_ALL"), @OpenApiParam(name = OVERRIDE_PROTECTION, type = Boolean.class, description = "Override protection" + " for the time series profile instance. Default is false"), + @OpenApiParam(name = TIMEZONE, description = "Specifies " + + "the time zone of the values of " + VERSION_DATE + " fields (unless " + + "otherwise specified). If this field is not specified, the default time zone " + + "of UTC shall be used.\r\nIgnored if " + VERSION_DATE + " was specified with " + + "offset and timezone."), @OpenApiParam(name = VERSION_DATE, type = Instant.class, description = "The version date of the" + " time series profile instance. Accepts ISO8601 format.", required = true), @OpenApiParam(name = PROFILE_DATA, required = true, description = "The profile data of the" 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 index 1fd980b8e..6f929a2d9 100644 --- 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 @@ -36,6 +36,7 @@ import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import cwms.cda.api.BaseHandler; import cwms.cda.api.Controllers; import cwms.cda.data.dao.LookupTypeDao; import cwms.cda.data.dao.watersupply.WaterSupplyAccountingDao; @@ -64,16 +65,11 @@ import org.jooq.DSLContext; -public class AccountingCreateController implements Handler { +public class AccountingCreateController extends BaseHandler { private static final String TAG = "Pump Accounting"; - private final MetricRegistry metrics; - - private Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); - } public AccountingCreateController(MetricRegistry metrics) { - this.metrics = metrics; + super(metrics); } @NotNull @@ -107,6 +103,8 @@ protected WaterSupplyAccountingDao getWaterSupplyAccountingDao(DSLContext dsl) { @Override public void handle(@NotNull Context ctx) { + logUnusedPathParameter(ctx, WATER_USER, "Body contains required information."); + try (Timer.Context ignored = markAndTime(CREATE)) { final String contractId = ctx.pathParam(CONTRACT_NAME); final String office = ctx.pathParam(OFFICE); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractCatalogController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractCatalogController.java index 8e31f1dbf..26a1d0e68 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractCatalogController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractCatalogController.java @@ -54,10 +54,10 @@ import org.jooq.DSLContext; -public final class WaterContractCatalogController extends WaterSupplyControllerBase implements Handler { +public final class WaterContractCatalogController extends WaterSupplyControllerBase { public WaterContractCatalogController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractController.java index 9b208dc28..43263274a 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractController.java @@ -56,10 +56,10 @@ import org.jooq.DSLContext; -public final class WaterContractController extends WaterSupplyControllerBase implements Handler { +public final class WaterContractController extends WaterSupplyControllerBase { public WaterContractController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractCreateController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractCreateController.java index 1748d2ed6..304511f30 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractCreateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractCreateController.java @@ -56,10 +56,10 @@ import org.jooq.DSLContext; -public final class WaterContractCreateController extends WaterSupplyControllerBase implements Handler { +public final class WaterContractCreateController extends WaterSupplyControllerBase { public WaterContractCreateController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( @@ -94,6 +94,8 @@ public WaterContractCreateController(MetricRegistry metrics) { @Override public void handle(@NotNull Context ctx) { + logUnusedPathParameter(ctx, PROJECT_ID, "Body contains required information."); + logUnusedPathParameter(ctx, OFFICE, "Body contains required information."); try (Timer.Context ignored = markAndTime(CREATE)) { DSLContext dsl = getDslContext(ctx); String formatHeader = ctx.req.getContentType(); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractDeleteController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractDeleteController.java index 409a39aa0..0b0b8304c 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractDeleteController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractDeleteController.java @@ -52,9 +52,9 @@ import org.jooq.DSLContext; -public final class WaterContractDeleteController extends WaterSupplyControllerBase implements Handler { +public final class WaterContractDeleteController extends WaterSupplyControllerBase { public WaterContractDeleteController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeCatalogController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeCatalogController.java index 7a2ffa4e1..ec92d906c 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeCatalogController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeCatalogController.java @@ -52,10 +52,10 @@ import org.jooq.DSLContext; -public final class WaterContractTypeCatalogController extends WaterSupplyControllerBase implements Handler { +public final class WaterContractTypeCatalogController extends WaterSupplyControllerBase { public WaterContractTypeCatalogController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeCreateController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeCreateController.java index 3e8bc44df..003375014 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeCreateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeCreateController.java @@ -50,10 +50,10 @@ import org.jooq.DSLContext; -public final class WaterContractTypeCreateController extends WaterSupplyControllerBase implements Handler { +public final class WaterContractTypeCreateController extends WaterSupplyControllerBase { public WaterContractTypeCreateController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeDeleteController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeDeleteController.java index 7a7849655..1ccf26aa4 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeDeleteController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractTypeDeleteController.java @@ -47,12 +47,12 @@ import org.jooq.DSLContext; -public final class WaterContractTypeDeleteController extends WaterSupplyControllerBase implements Handler { +public final class WaterContractTypeDeleteController extends WaterSupplyControllerBase { private static final String DISPLAY_VALUE = "display-value"; public WaterContractTypeDeleteController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractUpdateController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractUpdateController.java index ca6e6140d..d7faded49 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractUpdateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterContractUpdateController.java @@ -56,10 +56,10 @@ import org.jooq.DSLContext; -public final class WaterContractUpdateController extends WaterSupplyControllerBase implements Handler { +public final class WaterContractUpdateController extends WaterSupplyControllerBase { public WaterContractUpdateController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( @@ -96,6 +96,10 @@ public WaterContractUpdateController(MetricRegistry metrics) { @Override public void handle(@NotNull Context ctx) { + logUnusedPathParameter(ctx, PROJECT_ID, "Body contains required information."); + logUnusedPathParameter(ctx, OFFICE, "Body contains required information."); + logUnusedPathParameter(ctx, WATER_USER, "Body contains required information."); + try (Timer.Context ignored = markAndTime(UPDATE)) { DSLContext dsl = getDslContext(ctx); String contractName = ctx.pathParam(CONTRACT_NAME); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterPumpDisassociateController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterPumpDisassociateController.java index 5c3de2922..1c1dffc2f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterPumpDisassociateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterPumpDisassociateController.java @@ -52,12 +52,12 @@ import org.jooq.DSLContext; -public final class WaterPumpDisassociateController extends WaterSupplyControllerBase implements Handler { +public final class WaterPumpDisassociateController extends WaterSupplyControllerBase { private static final String PUMP_TYPE = "pump-type"; private static final String DELETE_ACCOUNTING = "delete-accounting"; public WaterPumpDisassociateController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterSupplyControllerBase.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterSupplyControllerBase.java index 7ff2d8aad..1e521d4a4 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterSupplyControllerBase.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterSupplyControllerBase.java @@ -28,23 +28,19 @@ import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import cwms.cda.api.BaseHandler; import cwms.cda.api.Controllers; import cwms.cda.data.dao.watersupply.WaterContractDao; import org.jooq.DSLContext; -public class WaterSupplyControllerBase { +public abstract class WaterSupplyControllerBase extends BaseHandler { static final String TAG = "Water Contracts"; - private MetricRegistry metrics; - WaterContractDao getContractDao(DSLContext dsl) { - return new WaterContractDao(dsl); - } - - Timer.Context markAndTime(String subject) { - return Controllers.markAndTime(metrics, getClass().getName(), subject); + public WaterSupplyControllerBase(MetricRegistry metrics) { + super(metrics); } - void waterMetrics(MetricRegistry metrics) { - this.metrics = metrics; + WaterContractDao getContractDao(DSLContext dsl) { + return new WaterContractDao(dsl); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserCatalogController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserCatalogController.java index 758ca2f00..253564b11 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserCatalogController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserCatalogController.java @@ -56,10 +56,10 @@ import org.jooq.DSLContext; -public final class WaterUserCatalogController extends WaterSupplyControllerBase implements Handler { +public final class WaterUserCatalogController extends WaterSupplyControllerBase { public WaterUserCatalogController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserController.java index 90886a3ca..d0de71695 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserController.java @@ -56,10 +56,10 @@ import org.jooq.DSLContext; -public final class WaterUserController extends WaterSupplyControllerBase implements Handler { +public final class WaterUserController extends WaterSupplyControllerBase { public WaterUserController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserCreateController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserCreateController.java index 4516a7ada..ff6e36fde 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserCreateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserCreateController.java @@ -49,10 +49,10 @@ import org.jooq.DSLContext; -public final class WaterUserCreateController extends WaterSupplyControllerBase implements Handler { +public final class WaterUserCreateController extends WaterSupplyControllerBase { public WaterUserCreateController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserDeleteController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserDeleteController.java index 2fece833e..aaacb4633 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserDeleteController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserDeleteController.java @@ -50,11 +50,11 @@ import org.jooq.DSLContext; -public final class WaterUserDeleteController extends WaterSupplyControllerBase implements Handler { +public final class WaterUserDeleteController extends WaterSupplyControllerBase { public WaterUserDeleteController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserUpdateController.java b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserUpdateController.java index 32bdc4a9e..1f69eff36 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserUpdateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/watersupply/WaterUserUpdateController.java @@ -58,10 +58,10 @@ import org.jooq.DSLContext; -public final class WaterUserUpdateController extends WaterSupplyControllerBase implements Handler { +public final class WaterUserUpdateController extends WaterSupplyControllerBase { public WaterUserUpdateController(MetricRegistry metrics) { - waterMetrics(metrics); + super(metrics); } @OpenApi( diff --git a/cwms-data-api/src/test/java/cwms/cda/api/OpenApiDocTest.java b/cwms-data-api/src/test/java/cwms/cda/api/OpenApiDocTest.java index 2d365d4c0..48322cde5 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/OpenApiDocTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/OpenApiDocTest.java @@ -22,8 +22,10 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.expr.ArrayInitializerExpr; import com.github.javaparser.ast.expr.ClassExpr; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.FieldAccessExpr; @@ -31,6 +33,7 @@ import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; +import com.github.javaparser.resolution.types.ResolvedType; import com.google.common.flogger.FluentLogger; import helpers.OpenApiDocInfo; import helpers.OpenApiDocTestInfo; @@ -39,6 +42,7 @@ import helpers.OpenApiParamUsageInfo; import helpers.OpenApiTestHelper; import io.javalin.apibuilder.CrudHandler; +import io.javalin.http.Context; import io.javalin.http.Handler; import java.io.IOException; import java.lang.reflect.Field; @@ -46,6 +50,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -53,7 +58,6 @@ import java.util.stream.Stream; import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.params.ParameterizedTest; @@ -79,9 +83,9 @@ void test_crud_handler_documentation(OpenApiDocTestInfo testInfo) throws IOExcep assertAll(buildTestAssertions(compilationUnit, testInfo)); } - @Test +// @Test void test_time_series_controller() throws IOException { - OpenApiDocTestInfo testInfo = OpenApiTestHelper.readOpenApiDocs(CrudHandler.class, StateController.class); + OpenApiDocTestInfo testInfo = OpenApiTestHelper.readOpenApiDocs(Handler.class, TimeSeriesFilteredController.class); CompilationUnit compilationUnit = OpenApiTestHelper.readCompilationUnit(testInfo.getClazz()); assertAll(buildTestAssertions(compilationUnit, testInfo)); } @@ -98,7 +102,7 @@ private Executable validateOpenApiDoc(CompilationUnit unit, OpenApiDocInfo testI output = testIgnoredMethod(unit, testInfo, clazz); } else { OpenApiParamUsage parsedParamInfo = parseParamInfo(unit, clazz, testInfo.getMethod()); - output = testMethod(testInfo, parsedParamInfo); + output = testMethod(unit, testInfo, parsedParamInfo, clazz); } return output; } @@ -119,6 +123,7 @@ private Executable testIgnoredMethod(CompilationUnit unit, OpenApiDocInfo testIn .filter(exp -> exp.getNameAsString().equals("json")) .findFirst(); + String methodRef = buildMethodRef(method, clazz); try { boolean usesStatus = statusCall.isPresent(); boolean isCorrectCode = statusCall.stream() @@ -131,25 +136,33 @@ private Executable testIgnoredMethod(CompilationUnit unit, OpenApiDocInfo testIn .map("CdaError.notImplemented()"::equals) .orElse(false); return () -> assertAll( - "Testing ignored method " + method.getNameAsString() + ": Incorrect response for ignored endpoint. Expecting `ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented())`", + "Testing ignored method " + method.getNameAsString() + " " + methodRef + ": Incorrect response for ignored endpoint. Expecting `ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented())`", () -> assertTrue(usesStatus && isCorrectCode, "Incorrect status code used, context should provide HttpServletResponse.SC_NOT_IMPLEMENTED."), () -> assertTrue(usesJson && isCorrectJson, "Incorrect JSON returned, context should respond with CdaError.notImplemented()")); } catch (Exception ex) { - return () -> fail("Testing ignored method " + method.getNameAsString() + ": Error analyzing method. Expected `ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented());`.", ex); + return () -> fail("Testing ignored method " + method.getNameAsString() + " " + methodRef + ": Error analyzing method. Expected `ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented());`.", ex); } } - private Executable testMethod(OpenApiDocInfo testInfo, - OpenApiParamUsage parsedParamInfo) { + private String buildMethodRef(MethodDeclaration method, Class clazz) { + //Creates a link in IntelliJ logs so we can easily jump to the file and declaration. + return "(" + clazz.getSimpleName() + ".java:" + method.getName().getBegin().map(p -> p.line).orElse(-1) + ")"; + } + + private Executable testMethod(CompilationUnit unit, OpenApiDocInfo testInfo, + OpenApiParamUsage parsedParamInfo, Class clazz) { List expectedQueryParameters = testInfo.getQueryParameters(); List expectedPathParameters = testInfo.getPathParameters(); Set receivedQueryParameters = parsedParamInfo.getQueryParams(); Set receivedPathParameters = parsedParamInfo.getPathParams(); OpenApiParamUsageInfo receivedResourceId = parsedParamInfo.getResourceId(); - return () -> assertAll("Testing " + testInfo.getMethod().getName(), + MethodDeclaration method = getMethodDeclaration(unit, testInfo.getMethod()); + String methodRef = buildMethodRef(method, clazz); + + return () -> assertAll("Testing " + testInfo.getMethod().getName() + " " + methodRef, () -> testQueryParameters(expectedQueryParameters, receivedQueryParameters), () -> testPathParameters(expectedPathParameters, receivedPathParameters, receivedResourceId)); } @@ -194,9 +207,11 @@ private void testPathParameters(List expectedPathParameters, S String missingInfo = missingItems.stream() .map(OpenApiParamInfo::getName) .collect(Collectors.joining(", ")); - assertAll(() -> assertTrue(receivedItems.isEmpty(), "Found used undocumented path parameter: " + extraInfo), + assertAll( + () -> assertTrue(receivedItems.isEmpty(), "Found used undocumented path parameter: " + extraInfo), () -> assertTrue(missingItems.isEmpty(), "Found documented path parameter that is not used: " + missingInfo), - () -> assertAll(expectedParams.stream().map(expectedParam -> testParamInfo(expectedParam, verifiedUsages)))); + () -> assertAll(expectedParams.stream().map(expectedParam -> testParamInfo(expectedParam, verifiedUsages))) + ); } private void testQueryParameters(List expectedQueryParameters, @@ -256,8 +271,10 @@ private OpenApiParamUsage parseParamInfo(CompilationUnit unit, Class clazz, M String context = methodDeclaration.getParameter(0).getNameAsString(); List methodCalls = methodDeclaration.findAll(MethodCallExpr.class); - Set optionalTypedQueryParams = readParamUsagesFromCall(methodCalls, call -> readQueryParamAsClassFromCall(unit, context, clazz, call), "queryParamAsClass"); + Set optionalTypedQueryParams = readParamUsagesSetFromCall(methodCalls, call -> readQueryParamAsClassFromCall(unit, context, clazz, call), "queryParamAsClass"); Set optionalDoubleQueryParams = readParamUsagesFromCall(methodCalls, call -> readUsageFromCall(unit, clazz, call, false), "queryParamAsDouble"); + Set filteredTsParam = readParamUsagesFromCall(methodCalls, this::findTsParamsFromUsage, "from"); + Set ignoredPathParams = readParamUsagesFromCall(methodCalls, this::readIgnoredPathParameter, "logUnusedPathParameter"); Set optionalStringQueryParams = methodCalls.stream() .filter(call -> call.getNameAsString().equals("queryParam")) @@ -290,12 +307,14 @@ private OpenApiParamUsage parseParamInfo(CompilationUnit unit, Class clazz, M queryParams.addAll(optionalTimeQueryParams); queryParams.addAll(requiredTimeQueryParams); queryParams.addAll(optionalDoubleQueryParams); + queryParams.addAll(filteredTsParam); Set pathParams = methodCalls.stream() .filter(call -> call.getNameAsString().equals("pathParam")) .map(call -> readUsageFromCall(unit, clazz, call, true)) .collect(Collectors.toSet()); + pathParams.addAll(ignoredPathParams); OpenApiParamUsageInfo resourceId = null; @@ -312,11 +331,31 @@ private OpenApiParamUsage parseParamInfo(CompilationUnit unit, Class clazz, M return new OpenApiParamUsage(pathParams, queryParams, resourceId); } - private OpenApiParamUsageInfo readQueryParamAsClassFromCall(CompilationUnit unit, String context, Class clazz, MethodCallExpr call) { + private OpenApiParamUsageInfo findTsParamsFromUsage(MethodCallExpr call) { + boolean isRightFunc = call.getScope() + .filter(Expression::isFieldAccessExpr) + .map(Expression::asFieldAccessExpr) + .map(s -> s.toString().equals("FilteredTimeSeriesParameters.Builder")) + .orElse(false); + OpenApiParamUsageInfo output = null; + if (isRightFunc) { + Expression arg0 = call.getArgument(0); + ResolvedType type = arg0.calculateResolvedType(); + if (type.isReferenceType()) { + String qualifiedName = type.asReferenceType().getQualifiedName(); + if (qualifiedName.equalsIgnoreCase(Context.class.getName())) { + output = new OpenApiParamUsageInfo(new OpenApiParamInfo(Controllers.QUERY, false, String.class), true, true); + } + } + } + return output; + } + + private Set readQueryParamAsClassFromCall(CompilationUnit unit, String context, Class clazz, MethodCallExpr call) { return call.getScope() .map(scope -> { if (scope.isNameExpr()) { - return readQueryParamAsClassFromContextCall(unit, clazz, call); + return Set.of(readQueryParamAsClassFromContextCall(unit, clazz, call)); } else { return readQueryParamAsClassFromControllersCall(unit, clazz, call); } @@ -338,24 +377,43 @@ private OpenApiParamUsageInfo readQueryParamAsClassFromContextCall(CompilationUn return new OpenApiParamUsageInfo(new OpenApiParamInfo(paramName, false, paramClass), used, nullHandled); } - private OpenApiParamUsageInfo readQueryParamAsClassFromControllersCall(CompilationUnit unit, Class clazz, MethodCallExpr call) { + private Set readQueryParamAsClassFromControllersCall(CompilationUnit unit, Class clazz, MethodCallExpr call) { Expression arg1 = call.getArgument(1); - Class type; - String name; + Set output = new HashSet<>(); if (arg1.isArrayCreationExpr()) { //Context, String[], Class, T, {metrics}, {className} - type = identifyClassFromExpression(unit, clazz, call.getArgument(2).asClassExpr()); - name = parseParameterName(arg1.asArrayCreationExpr().getInitializer().orElse(null).getValues().get(0)); + NodeList values = arg1.asArrayCreationExpr() + .getInitializer() + .map(ArrayInitializerExpr::getValues) + .orElse(new NodeList<>()); + Class type = identifyClassFromExpression(unit, clazz, call.getArgument(2).asClassExpr()); + values.forEach(value -> { + String name = parseParameterName(value); + output.add(new OpenApiParamUsageInfo(new OpenApiParamInfo(name, false, type), true, true)); + }); } else if (arg1.isClassExpr()) { //Context, Class, T, Name, [Aliases] - type = identifyClassFromExpression(unit, clazz, arg1.asClassExpr()); - name = parseParameterName(call.getArgument(3)); + Class type = identifyClassFromExpression(unit, clazz, arg1.asClassExpr()); + String name = parseParameterName(call.getArgument(3)); + output.add(new OpenApiParamUsageInfo(new OpenApiParamInfo(name, false, type), true, true)); } else { //Unknown case for queryParamAsClass (new method to handle? throw new UnsupportedOperationException("Unsupported argument[1] type for queryParamAsClass: " + arg1.getClass()); } - return new OpenApiParamUsageInfo(new OpenApiParamInfo(name, false, type), true, true); + return output; + } + + private Set readParamUsagesSetFromCall(List methodCalls, + Function> paramReader, + String... functions) { + List realFunctions = Arrays.asList(functions); + return methodCalls.stream() + .filter(call -> realFunctions.contains(call.getNameAsString())) + .map(paramReader) + .filter(s -> !s.isEmpty()) + .flatMap(Set::stream) + .collect(Collectors.toSet()); } private Set readParamUsagesFromCall(List methodCalls, @@ -365,6 +423,7 @@ private Set readParamUsagesFromCall(List return methodCalls.stream() .filter(call -> realFunctions.contains(call.getNameAsString())) .map(paramReader) + .filter(Objects::nonNull) .collect(Collectors.toSet()); } @@ -378,6 +437,12 @@ private Set readJavaTimeFromCall(MethodCallExpr call, boo new OpenApiParamUsageInfo(new OpenApiParamInfo(Controllers.TIMEZONE, required, type), used, nullHandled)); } + private OpenApiParamUsageInfo readIgnoredPathParameter(MethodCallExpr call) { + //Should never have scope, formatted as logUnusedPathParameter(Context ctx, String pathParam, String reason) + String param = parseParameterName(call.getArgument(1)); + return new OpenApiParamUsageInfo(new OpenApiParamInfo(param, true, String.class), true, true); + } + private OpenApiParamUsageInfo readUsageFromCall(CompilationUnit unit, Class clazz, MethodCallExpr call, boolean required) { //We have a scope, so it's called from something like context. return call.getScope().map(exp -> {