Skip to content

Commit 134a4af

Browse files
authored
REGI-469: Filtering Parameters for Location getAll Method (#1211)
REGI-469: Implement AtFlowProducingLocationManager::retrieveValidLocations Adds location kind exclusion parameter and base location exclusion parameter to Location catalog method. Allows for filtering unwanted kinds and filtering to only include sub-locations.
1 parent a37edbf commit 134a4af

13 files changed

Lines changed: 485 additions & 75 deletions

cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,7 @@
11
package cwms.cda.api;
22

33
import static com.codahale.metrics.MetricRegistry.name;
4-
import static cwms.cda.api.Controllers.ACCEPT;
5-
import static cwms.cda.api.Controllers.INCLUDE_ALIASES;
6-
import static cwms.cda.api.Controllers.BOUNDING_OFFICE_LIKE;
7-
import static cwms.cda.api.Controllers.CURSOR;
8-
import static cwms.cda.api.Controllers.EXCLUDE_EMPTY;
9-
import static cwms.cda.api.Controllers.GET_ONE;
10-
import static cwms.cda.api.Controllers.INCLUDE_EXTENTS;
11-
import static cwms.cda.api.Controllers.LIKE;
12-
import static cwms.cda.api.Controllers.LOCATIONS;
13-
import static cwms.cda.api.Controllers.LOCATION_CATEGORY_LIKE;
14-
import static cwms.cda.api.Controllers.LOCATION_GROUP_LIKE;
15-
import static cwms.cda.api.Controllers.LOCATION_KIND_LIKE;
16-
import static cwms.cda.api.Controllers.LOCATION_TYPE_LIKE;
17-
import static cwms.cda.api.Controllers.OFFICE;
18-
import static cwms.cda.api.Controllers.PAGE;
19-
import static cwms.cda.api.Controllers.PAGE_SIZE;
20-
import static cwms.cda.api.Controllers.RESULTS;
21-
import static cwms.cda.api.Controllers.SIZE;
22-
import static cwms.cda.api.Controllers.STATUS_200;
23-
import static cwms.cda.api.Controllers.TIMESERIES;
24-
import static cwms.cda.api.Controllers.TIMESERIES_CATEGORY_LIKE;
25-
import static cwms.cda.api.Controllers.TIMESERIES_GROUP_LIKE;
26-
import static cwms.cda.api.Controllers.UNIT_SYSTEM;
27-
import static cwms.cda.api.Controllers.queryParamAsClass;
4+
import static cwms.cda.api.Controllers.*;
285

296
import com.codahale.metrics.Histogram;
307
import com.codahale.metrics.MetricRegistry;
@@ -165,6 +142,13 @@ public void getAll(Context ctx) {
165142
+ "by using Regular Expression OR clauses. For example: "
166143
+ "\"(SITE|STREAM)\""
167144
),
145+
@OpenApiParam(name = FILTER_BASE_LOCATIONS, type = Boolean.class,
146+
description = "Specifies whether to filter the locations based on the "
147+
+ "base location. Default: false. If true, only sublocations "
148+
+ "locations will be returned. If false, all locations will be returned. "
149+
+ "Only supported for JSON format."),
150+
@OpenApiParam(name = NEGATE_LOCATION_KIND_LIKE, description = "Whether to use the location kind "
151+
+ "regular expression to exclude locations with the specified kinds. Default is false."),
168152
@OpenApiParam(name = LOCATION_TYPE_LIKE,
169153
description = "Posix <a href=\"regexp.html\">regular expression</a> matching "
170154
+ "against the location type."
@@ -233,6 +217,12 @@ public void getOne(@NotNull Context ctx, @NotNull String dataSet) {
233217
String locationKind = queryParamAsClass(ctx, new String[]{LOCATION_KIND_LIKE},
234218
String.class, null, metrics, name(CatalogController.class.getName(), GET_ONE));
235219

220+
boolean negateLocationKind = ctx.queryParamAsClass(NEGATE_LOCATION_KIND_LIKE, Boolean.class)
221+
.getOrDefault(false);
222+
223+
boolean filterBaseLocations = ctx.queryParamAsClass(FILTER_BASE_LOCATIONS, Boolean.class)
224+
.getOrDefault(false);
225+
236226
String locationType = queryParamAsClass(ctx, new String[]{LOCATION_TYPE_LIKE},
237227
String.class, null, metrics, name(CatalogController.class.getName(), GET_ONE));
238228
boolean includeAliases = ctx.queryParamAsClass(INCLUDE_ALIASES, Boolean.class)
@@ -279,6 +269,8 @@ public void getOne(@NotNull Context ctx, @NotNull String dataSet) {
279269
.withBoundingOfficeLike(boundingOfficeLike)
280270
.withLocationKind(locationKind)
281271
.withLocationType(locationType)
272+
.withFilterBaseLocations(filterBaseLocations)
273+
.withNegateLocationKindLike(negateLocationKind)
282274
.build();
283275

284276
LocationsDao dao = new LocationsDaoImpl(dsl);

cwms-data-api/src/main/java/cwms/cda/api/Controllers.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ public final class Controllers {
145145
public static final String ISSUE_DATE = "issue-date";
146146
public static final String LOCATION_KIND_LIKE = "location-kind-like";
147147
public static final String LOCATION_TYPE_LIKE = "location-type-like";
148+
public static final String NEGATE_LOCATION_KIND_LIKE = "negate-location-kind-like";
148149
public static final String INCLUDE_ALIASES = "include-aliases";
149150
public static final String MIN_NUMBER = "min-number";
150151
public static final String MAX_NUMBER = "max-number";
@@ -154,7 +155,9 @@ public final class Controllers {
154155
public static final String MAX_FLOW = "max-flow";
155156
public static final String AGENCY = "agency";
156157
public static final String QUALITY = "quality";
157-
158+
public static final String NAMES = "names";
159+
public static final String EXCLUDE_KINDS = "exclude-kinds";
160+
public static final String FILTER_BASE_LOCATIONS = "filter-base-locations";
158161

159162
public static final String GROUP_ID = "group-id";
160163
public static final String REPLACE_ASSIGNED_LOCS = "replace-assigned-locs";

cwms-data-api/src/main/java/cwms/cda/api/LocationController.java

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,7 @@
2525
package cwms.cda.api;
2626

2727
import static com.codahale.metrics.MetricRegistry.name;
28-
import static cwms.cda.api.Controllers.CASCADE_DELETE;
29-
import static cwms.cda.api.Controllers.CREATE;
30-
import static cwms.cda.api.Controllers.DATUM;
31-
import static cwms.cda.api.Controllers.DELETE;
32-
import static cwms.cda.api.Controllers.FAIL_IF_EXISTS;
33-
import static cwms.cda.api.Controllers.FORMAT;
34-
import static cwms.cda.api.Controllers.GET_ALL;
35-
import static cwms.cda.api.Controllers.GET_ONE;
36-
import static cwms.cda.api.Controllers.OFFICE;
37-
import static cwms.cda.api.Controllers.RESULTS;
38-
import static cwms.cda.api.Controllers.SIZE;
39-
import static cwms.cda.api.Controllers.STATUS_200;
40-
import static cwms.cda.api.Controllers.STATUS_404;
41-
import static cwms.cda.api.Controllers.UNIT;
42-
import static cwms.cda.api.Controllers.UPDATE;
43-
import static cwms.cda.api.Controllers.VERSION;
28+
import static cwms.cda.api.Controllers.*;
4429
import static cwms.cda.api.Controllers.addDeprecatedContentTypeWarning;
4530
import static cwms.cda.data.dao.JooqDao.getDslContext;
4631

@@ -59,7 +44,6 @@
5944
import cwms.cda.api.errors.CdaError;
6045
import cwms.cda.api.errors.DeleteConflictException;
6146
import cwms.cda.api.errors.NotFoundException;
62-
import cwms.cda.data.dao.JooqDao;
6347
import cwms.cda.data.dao.LocationsDao;
6448
import cwms.cda.data.dao.LocationsDaoImpl;
6549
import cwms.cda.data.dto.Location;
@@ -91,7 +75,6 @@
9175

9276
public class LocationController implements CrudHandler {
9377
public static final Logger logger = Logger.getLogger(LocationController.class.getName());
94-
public static final String NAMES = "names";
9578
private final MetricRegistry metrics;
9679

9780
private final Histogram requestResultSize;
@@ -120,7 +103,7 @@ private Timer.Context markAndTime(String subject) {
120103
+ ". If this field is not specified, matching location level "
121104
+ "information from all offices shall be returned."),
122105
@OpenApiParam(name = UNIT, description = "Specifies the unit or unit system"
123-
+ " of the response. Valid values for the unit field are:"
106+
+ " of the response. Default is SI. Valid values for the unit field are:"
124107
+ "\n* `EN` Specifies English unit system. Location level values will be in"
125108
+ " the default English units for their parameters."
126109
+ "\n* `SI` Specifies the SI unit system. Location level values will be in "
@@ -308,7 +291,8 @@ public void create(@NotNull Context ctx) {
308291
Location locationFromBody = Formats.parseContent(contentType, ctx.body(), Location.class);
309292
boolean failIfExists = ctx.queryParamAsClass(FAIL_IF_EXISTS, Boolean.class).getOrDefault(true);
310293
locationsDao.storeLocation(locationFromBody, failIfExists);
311-
StatusResponse re = new StatusResponse(locationFromBody.getOfficeId(),"Created Location", locationFromBody.getName());
294+
StatusResponse re = new StatusResponse(locationFromBody.getOfficeId(),
295+
"Created Location", locationFromBody.getName());
312296
ctx.status(HttpServletResponse.SC_CREATED).json(re);
313297
} catch (IOException ex) {
314298
CdaError re = new CdaError("failed to process request");

cwms-data-api/src/main/java/cwms/cda/data/dao/CatalogRequestParameters.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public class CatalogRequestParameters {
2121
private final String locationKind;
2222
private final String locationType;
2323
private final boolean includeAliases;
24+
private final boolean filterBaseLocations;
25+
private final boolean negateLocationKindLike;
2426

2527
private CatalogRequestParameters(Builder builder) {
2628
this.office = builder.office;
@@ -36,6 +38,8 @@ private CatalogRequestParameters(Builder builder) {
3638
this.locationKind = builder.locationKind;
3739
this.locationType = builder.locationType;
3840
this.includeAliases = builder.includeAliases;
41+
this.filterBaseLocations = builder.filterBaseLocations;
42+
this.negateLocationKindLike = builder.negateLocationKindLike;
3943
}
4044

4145
public String getBoundingOfficeLike() {
@@ -90,6 +94,13 @@ public boolean includeAliases() {
9094
return includeAliases;
9195
}
9296

97+
public boolean filterBaseLocations() {
98+
return filterBaseLocations;
99+
}
100+
101+
public boolean isNegateLocationKindLike() {
102+
return negateLocationKindLike;
103+
}
93104

94105
public static class Builder {
95106
String office;
@@ -105,6 +116,8 @@ public static class Builder {
105116
String locationKind;
106117
String locationType;
107118
private boolean includeAliases = false;
119+
private boolean filterBaseLocations = false;
120+
private boolean negateLocationKindLike = false;
108121

109122
public Builder() {
110123

@@ -175,6 +188,16 @@ public Builder withIncludeAliases(boolean includeAliases) {
175188
return this;
176189
}
177190

191+
public Builder withFilterBaseLocations(boolean filterBaseLocations) {
192+
this.filterBaseLocations = filterBaseLocations;
193+
return this;
194+
}
195+
196+
public Builder withNegateLocationKindLike(boolean negateLocationKindLike) {
197+
this.negateLocationKindLike = negateLocationKindLike;
198+
return this;
199+
}
200+
178201
public static Builder from(CatalogRequestParameters params) {
179202
// This NEEDS to include every field in the CatalogRequestParameters
180203
return new Builder()
@@ -190,6 +213,8 @@ public static Builder from(CatalogRequestParameters params) {
190213
.withExcludeEmpty(params.excludeEmpty)
191214
.withLocationKind(params.locationKind)
192215
.withLocationType(params.locationType)
216+
.withFilterBaseLocations(params.filterBaseLocations)
217+
.withNegateLocationKindLike(params.negateLocationKindLike)
193218
;
194219
}
195220

cwms-data-api/src/main/java/cwms/cda/data/dao/JooqDao.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424

2525
package cwms.cda.data.dao;
2626

27-
import java.io.IOException;
28-
import java.io.InputStream;
2927
import java.sql.Timestamp;
3028
import java.time.Instant;
3129

@@ -47,17 +45,12 @@
4745
import java.sql.Connection;
4846
import java.sql.SQLClientInfoException;
4947
import java.sql.SQLException;
50-
import java.sql.Timestamp;
5148
import java.time.DateTimeException;
52-
import java.time.Instant;
5349
import java.time.ZoneId;
5450
import java.util.Arrays;
5551
import java.util.Collections;
56-
import java.util.HashMap;
5752
import java.util.List;
58-
import java.util.Map;
5953
import java.util.Optional;
60-
import java.util.Properties;
6154
import java.util.regex.Matcher;
6255
import java.util.regex.Pattern;
6356
import javax.servlet.http.HttpServletResponse;
@@ -231,10 +224,19 @@ public static Condition caseInsensitiveLikeRegex(Field<String> field, String reg
231224
return new CustomCondition() {
232225
@Override
233226
public void accept(org.jooq.Context<?> ctx) {
234-
if (ctx.family() == ORACLE) {
235-
ctx.visit(DSL.condition("{regexp_like}({0}, {1}, 'i')", field, DSL.val(regex)));
227+
if (regex.toUpperCase().startsWith("NOT:")) {
228+
String finalRegex = regex.substring(4);
229+
if (ctx.family() == ORACLE) {
230+
ctx.visit(DSL.condition("{not regexp_like}({0}, {1}, 'i')", field, DSL.val(finalRegex)));
231+
} else {
232+
ctx.visit(DSL.upper(field).notLikeRegex(finalRegex.toUpperCase()));
233+
}
236234
} else {
237-
ctx.visit(DSL.upper(field).likeRegex(regex.toUpperCase()));
235+
if (ctx.family() == ORACLE) {
236+
ctx.visit(DSL.condition("{regexp_like}({0}, {1}, 'i')", field, DSL.val(regex)));
237+
} else {
238+
ctx.visit(DSL.upper(field).likeRegex(regex.toUpperCase()));
239+
}
238240
}
239241
}
240242
};
@@ -389,12 +391,10 @@ static InvalidItemException buildInvalidTSIDIntervalException(RuntimeException i
389391
String localizedMessage = cause.getLocalizedMessage();
390392

391393
if (localizedMessage != null) {
392-
if (localizedMessage != null) {
393-
String[] parts = localizedMessage.split("\n");
394-
if (parts.length > 2) {
395-
return new InvalidItemException(String.format("Invalid Time Series Description: %s is not a valid interval",
396-
parts[1]), cause);
397-
}
394+
String[] parts = localizedMessage.split("\n");
395+
if (parts.length > 2) {
396+
return new InvalidItemException(String.format("Invalid Time Series Description: %s is not a valid interval",
397+
parts[1]), cause);
398398
}
399399
}
400400
return new InvalidItemException("Invalid Time Series Description", cause);

cwms-data-api/src/main/java/cwms/cda/data/dao/LocationsDaoImpl.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ public String getLocations(String names, String format, String units, String dat
108108
names, format, units, datum, officeId);
109109
}
110110

111-
@Override
112111
public List<Location> getLocations(String nameRegex, String unitSystem, String datum, String officeId) {
113112

114113
Condition whereCondition = JooqDao.caseInsensitiveLikeRegexNullTrue(AV_LOC.LOCATION_ID, nameRegex);
@@ -504,11 +503,21 @@ private static Condition buildWhereCondition(CatalogRequestParameters params) {
504503

505504
condition = condition.and(caseInsensitiveLikeRegexNullTrue(AV_LOC2.AV_LOC2.BOUNDING_OFFICE_ID,
506505
params.getBoundingOfficeLike()));
506+
507+
String regexLocationKind = params.getLocationKind();
508+
if (params.isNegateLocationKindLike() && !regexLocationKind.toUpperCase().startsWith("NOT:")) {
509+
regexLocationKind = String.format("NOT:%s", regexLocationKind);
510+
}
507511
condition = condition.and(caseInsensitiveLikeRegexNullTrue(AV_LOC2.AV_LOC2.LOCATION_KIND_ID,
508-
params.getLocationKind()));
512+
regexLocationKind));
513+
509514
condition = condition.and(caseInsensitiveLikeRegexNullTrue(AV_LOC2.AV_LOC2.LOCATION_TYPE,
510515
params.getLocationType()));
511516

517+
if (params.filterBaseLocations()) {
518+
condition = condition.and(AV_LOC2.AV_LOC2.SUB_LOCATION_ID.isNotNull());
519+
}
520+
512521
return condition;
513522
}
514523

cwms-data-api/src/main/webapp/regexp.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ <h2 class="box-header-striped">
4343
<a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/adfns/regexp.html">https://docs.oracle.com/en/database/oracle/oracle-database/19/adfns/regexp.html</a>
4444
<br>
4545
In Data API case-insensitive regular expressions are used.
46+
<br>
47+
Due to limited support with Oracle regex, a custom negation option has been implemented. When prepended with "<b>NOT:</b>", regex parameters are negated,
48+
thus providing functionality opposite to the default behavior.
4649
</div>
4750
</div>
4851
</div>

0 commit comments

Comments
 (0)