diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 476277151..107bc219f 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -300,7 +300,7 @@ paths: - name: q in: query - description: 'The attribute query is used for querying allowed quantitative properties.
Used to query on a value of a resource attribute using `<`,`>`,`<=`,`>=`,`!=`, `==` operators.
For e.g, attribute > value, attribute < value, attribute >= value, attribute <= value, attribute != value and attribute == value.
Allowed values for all operators is double.
For the operator `==` if the query is on `id` then the only value allowed is an `data exchange ID` of a resource.' + description: 'The attribute query is used for querying allowed quantitative properties.
Used to query on a value of a resource attribute using `<`,`>`,`<=`,`>=`,`!=`, `==` operators.
Additionally, a user can search a resource attribute with a alpha-numeric value using `==` operator. For e.g, attribute > value, attribute < value, attribute >= value, attribute <= value, attribute != value and attribute == value.
Allowed values for all operators is double, and as a special case alpha-nmeric values are allowed for `==`.
For the operator `==` if the query is on `id` then the only value allowed is an `data exchange ID` of a resource.' schema: type: string maxLength: 512 @@ -405,6 +405,12 @@ paths: curl --location -g --request GET 'https://example.com/ngsi-ld/v1/entities?id=UUID&geoproperty=location&georel=near;maxDistance=10&geometry=Point&coordinates=[21.178,72.834]&options=count' \ --header 'token: ' + - lang: 'cURL' + label: 'search by string attribute' + source: | + curl --location --request GET 'https://example.com/ngsi-ld/v1/entities?id=UUID&offset=0&limit=10&q=license_plate==GJ05BU3663' \ + --header 'token: ' + deprecated: false description: | @@ -452,7 +458,7 @@ paths: The attribute query is used for querying allowed quantitative properties. - Used to query on a value of a resource attribute using `<`,`>`,`<=`,`>=`,`!=`,`==` operators. - For e.g, attribute > value, attribute < value, attribute >= value, attribute <= value and attribute == value. - - Allowed values for all operators is double. + - Allowed values for all operators is double and alphanumeric for `==` operator. - For the operator `==` if the query is on `id` then the only value allowed is an `data exchange ID` of a resource. - e.g, `q=attribute-name>attribute-value` diff --git a/src/main/java/iudx/resource/server/apiserver/handlers/FailureHandler.java b/src/main/java/iudx/resource/server/apiserver/handlers/FailureHandler.java index 7d2b3b0b0..23b059644 100644 --- a/src/main/java/iudx/resource/server/apiserver/handlers/FailureHandler.java +++ b/src/main/java/iudx/resource/server/apiserver/handlers/FailureHandler.java @@ -29,7 +29,7 @@ public void handle(RoutingContext context) { new RestResponse.Builder() .withType(exception.getUrn().getUrn()) .withTitle(code.getDescription()) - .withMessage(code.getDescription()) + .withMessage(exception.getMessage()) .build() .toJson(); diff --git a/src/main/java/iudx/resource/server/apiserver/query/QueryMapper.java b/src/main/java/iudx/resource/server/apiserver/query/QueryMapper.java index 553bf69d0..e4cfaf7ed 100644 --- a/src/main/java/iudx/resource/server/apiserver/query/QueryMapper.java +++ b/src/main/java/iudx/resource/server/apiserver/query/QueryMapper.java @@ -1,19 +1,20 @@ package iudx.resource.server.apiserver.query; +import static iudx.resource.server.apiserver.util.Constants.*; import static iudx.resource.server.common.HttpStatusCode.BAD_REQUEST; -import static iudx.resource.server.common.ResponseUrn.INVALID_ATTR_PARAM_URN; -import static iudx.resource.server.common.ResponseUrn.INVALID_GEO_PARAM_URN; +import static iudx.resource.server.common.ResponseUrn.*; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import iudx.resource.server.apiserver.exceptions.DxRuntimeException; import iudx.resource.server.apiserver.util.Constants; +import iudx.resource.server.common.HttpStatusCode; import iudx.resource.server.common.ResponseUrn; import java.time.Duration; import java.time.ZonedDateTime; import java.util.Arrays; -import java.util.List; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -55,14 +56,14 @@ public JsonObject toJson(NgsildQueryParams params, boolean isTemporal, boolean i if (params.getId() != null) { JsonArray jsonArray = new JsonArray(); params.getId().forEach(s -> jsonArray.add(s.toString())); - json.put(Constants.JSON_ID, jsonArray); + json.put(JSON_ID, jsonArray); LOGGER.debug("Info : json " + json); } if (params.getAttrs() != null) { isResponseFilter = true; JsonArray jsonArray = new JsonArray(); params.getAttrs().forEach(attribute -> jsonArray.add(attribute)); - json.put(Constants.JSON_ATTRIBUTE_FILTER, jsonArray); + json.put(JSON_ATTRIBUTE_FILTER, jsonArray); LOGGER.debug("Info : json " + json); } if (isGeoQuery(params)) { @@ -71,23 +72,23 @@ public JsonObject toJson(NgsildQueryParams params, boolean isTemporal, boolean i && params.getGeometry() != null && params.getGeoProperty() != null) { isGeoSearch = true; - if (params.getGeometry().equalsIgnoreCase(Constants.GEOM_POINT) - && params.getGeoRel().getRelation().equals(Constants.JSON_NEAR) + if (params.getGeometry().equalsIgnoreCase(GEOM_POINT) + && params.getGeoRel().getRelation().equals(JSON_NEAR) && params.getGeoRel().getMaxDistance() != null) { String[] coords = params.getCoordinates().replaceAll("\\[|\\]", "").split(","); - json.put(Constants.JSON_LAT, Double.parseDouble(coords[0])); - json.put(Constants.JSON_LON, Double.parseDouble(coords[1])); - json.put(Constants.JSON_RADIUS, params.getGeoRel().getMaxDistance()); + json.put(JSON_LAT, Double.parseDouble(coords[0])); + json.put(JSON_LON, Double.parseDouble(coords[1])); + json.put(JSON_RADIUS, params.getGeoRel().getMaxDistance()); } else { - json.put(Constants.JSON_GEOMETRY, params.getGeometry()); - json.put(Constants.JSON_COORDINATES, params.getCoordinates()); + json.put(JSON_GEOMETRY, params.getGeometry()); + json.put(JSON_COORDINATES, params.getCoordinates()); json.put( - Constants.JSON_GEOREL, - getOrDefault(params.getGeoRel().getRelation(), Constants.JSON_WITHIN)); + JSON_GEOREL, + getOrDefault(params.getGeoRel().getRelation(), JSON_WITHIN)); if (params.getGeoRel().getMaxDistance() != null) { - json.put(Constants.JSON_MAXDISTANCE, params.getGeoRel().getMaxDistance()); + json.put(JSON_MAXDISTANCE, params.getGeoRel().getMaxDistance()); } else if (params.getGeoRel().getMinDistance() != null) { - json.put(Constants.JSON_MINDISTANCE, params.getGeoRel().getMinDistance()); + json.put(JSON_MINDISTANCE, params.getGeoRel().getMinDistance()); } } LOGGER.debug("Info : json " + json); @@ -106,22 +107,22 @@ public JsonObject toJson(NgsildQueryParams params, boolean isTemporal, boolean i && params.getTemporalRelation().getTemprel() != null && params.getTemporalRelation().getTime() != null) { isTemporal = true; - if (params.getTemporalRelation().getTemprel().equalsIgnoreCase(Constants.JSON_DURING) - || params.getTemporalRelation().getTemprel().equalsIgnoreCase(Constants.JSON_BETWEEN)) { + if (params.getTemporalRelation().getTemprel().equalsIgnoreCase(JSON_DURING) + || params.getTemporalRelation().getTemprel().equalsIgnoreCase(JSON_BETWEEN)) { LOGGER.debug("Info : inside during "); - json.put(Constants.JSON_TIME, params.getTemporalRelation().getTime()); - json.put(Constants.JSON_ENDTIME, params.getTemporalRelation().getEndTime()); - json.put(Constants.JSON_TIMEREL, params.getTemporalRelation().getTemprel()); + json.put(JSON_TIME, params.getTemporalRelation().getTime()); + json.put(JSON_ENDTIME, params.getTemporalRelation().getEndTime()); + json.put(JSON_TIMEREL, params.getTemporalRelation().getTemprel()); isValidTimeInterval( - Constants.JSON_DURING, - json.getString(Constants.JSON_TIME), - json.getString(Constants.JSON_ENDTIME), + JSON_DURING, + json.getString(JSON_TIME), + json.getString(JSON_ENDTIME), isAsyncQuery); } else { - json.put(Constants.JSON_TIME, params.getTemporalRelation().getTime().toString()); - json.put(Constants.JSON_TIMEREL, params.getTemporalRelation().getTemprel()); + json.put(JSON_TIME, params.getTemporalRelation().getTime().toString()); + json.put(JSON_TIMEREL, params.getTemporalRelation().getTemprel()); } LOGGER.debug("Info : json " + json); } @@ -132,25 +133,25 @@ public JsonObject toJson(NgsildQueryParams params, boolean isTemporal, boolean i for (String term : qterms) { query.add(getQueryTerms(term)); } - json.put(Constants.JSON_ATTR_QUERY, query); + json.put(JSON_ATTR_QUERY, query); LOGGER.debug("Info : json " + json); } if (params.getGeoProperty() != null) { - json.put(Constants.JSON_GEOPROPERTY, params.getGeoProperty()); + json.put(JSON_GEOPROPERTY, params.getGeoProperty()); LOGGER.debug("Info : json " + json); } if (params.getOptions() != null) { - json.put(Constants.IUDXQUERY_OPTIONS, params.getOptions()); + json.put(IUDXQUERY_OPTIONS, params.getOptions()); LOGGER.debug("Info : json " + json); } if (params.getPageFrom() != null) { - json.put(Constants.NGSILDQUERY_FROM, params.getPageFrom()); + json.put(NGSILDQUERY_FROM, params.getPageFrom()); } if (params.getPageSize() != null) { - json.put(Constants.NGSILDQUERY_SIZE, params.getPageSize()); + json.put(NGSILDQUERY_SIZE, params.getPageSize()); } - json.put(Constants.JSON_SEARCH_TYPE, getSearchType(isAsyncQuery)); + json.put(JSON_SEARCH_TYPE, getSearchType(isAsyncQuery)); LOGGER.debug("Info : json " + json); return json; } @@ -162,12 +163,12 @@ public JsonObject toJson(NgsildQueryParams params, boolean isTemporal, boolean i private void isValidTimeInterval( String timeRel, String time, String endTime, boolean isAsyncQuery) { long totalDaysAllowed = 0; - if (timeRel.equalsIgnoreCase(Constants.JSON_DURING)) { + if (timeRel.equalsIgnoreCase(JSON_DURING)) { if (isNullorEmpty(time) || isNullorEmpty(endTime)) { DxRuntimeException ex = new DxRuntimeException( BAD_REQUEST.getValue(), - ResponseUrn.INVALID_TEMPORAL_PARAM_URN, + INVALID_TEMPORAL_PARAM_URN, "time and endTime both are mandatory for during Query."); this.context.fail(400, ex); } @@ -181,25 +182,25 @@ private void isValidTimeInterval( DxRuntimeException exc = new DxRuntimeException( BAD_REQUEST.getValue(), - ResponseUrn.INVALID_TEMPORAL_PARAM_URN, + INVALID_TEMPORAL_PARAM_URN, "time and endTime both are mandatory for during Query."); this.context.fail(400, exc); } } if (isAsyncQuery - && totalDaysAllowed > Constants.VALIDATION_MAX_DAYS_INTERVAL_ALLOWED_FOR_ASYNC) { + && totalDaysAllowed > VALIDATION_MAX_DAYS_INTERVAL_ALLOWED_FOR_ASYNC) { DxRuntimeException ex = new DxRuntimeException( BAD_REQUEST.getValue(), - ResponseUrn.INVALID_TEMPORAL_PARAM_URN, + INVALID_TEMPORAL_PARAM_URN, "time interval greater than 1 year is not allowed"); this.context.fail(400, ex); } - if (!isAsyncQuery && totalDaysAllowed > Constants.VALIDATION_MAX_DAYS_INTERVAL_ALLOWED) { + if (!isAsyncQuery && totalDaysAllowed > VALIDATION_MAX_DAYS_INTERVAL_ALLOWED) { DxRuntimeException ex = new DxRuntimeException( BAD_REQUEST.getValue(), - ResponseUrn.INVALID_TEMPORAL_PARAM_URN, + INVALID_TEMPORAL_PARAM_URN, "time interval greater than 10 days is not allowed"); this.context.fail(400, ex); } @@ -228,59 +229,62 @@ private T getOrDefault(T value, T def) { private String getSearchType(boolean isAsyncQuery) { StringBuilder searchType = new StringBuilder(); if (isTemporal) { - searchType.append(Constants.JSON_TEMPORAL_SEARCH); + searchType.append(JSON_TEMPORAL_SEARCH); } else if (!isTemporal && !isAsyncQuery) { - searchType.append(Constants.JSON_LATEST_SEARCH); + searchType.append(JSON_LATEST_SEARCH); } if (isGeoSearch) { - searchType.append(Constants.JSON_GEO_SEARCH); + searchType.append(JSON_GEO_SEARCH); } if (isResponseFilter) { - searchType.append(Constants.JSON_RESPONSE_FILTER_SEARCH); + searchType.append(JSON_RESPONSE_FILTER_SEARCH); } if (isAttributeSearch) { - searchType.append(Constants.JSON_ATTRIBUTE_SEARCH); + searchType.append(JSON_ATTRIBUTE_SEARCH); } return searchType.toString().isEmpty() ? "" : searchType.substring(0, searchType.length() - 1).toString(); } - JsonObject getQueryTerms(String queryTerms) { + JsonObject getQueryTerms(final String queryTerms) { JsonObject json = new JsonObject(); - int length = queryTerms.length(); - List allowedSpecialCharacter = Arrays.asList('>', '=', '<', '!'); - List allowedOperators = Arrays.asList(">", "=", "<", ">=", "<=", "==", "!="); - int startIndex = 0; - boolean specialCharFound = false; - for (int i = 0; i < length; i++) { - Character c = queryTerms.charAt(i); - if (!(Character.isLetter(c) || Character.isDigit(c)) && !specialCharFound) { - if (allowedSpecialCharacter.contains(c)) { - json.put(Constants.JSON_ATTRIBUTE, queryTerms.substring(startIndex, i)); - startIndex = i; - specialCharFound = true; - } else { - LOGGER.debug("Ignore " + c.toString()); - DxRuntimeException ex = - new DxRuntimeException( - BAD_REQUEST.getValue(), INVALID_ATTR_PARAM_URN, "Operator not allowed."); - this.context.fail(400, ex); - } + String jsonOperator = ""; + String jsonValue = ""; + String jsonAttribute = ""; + + String[] attributes = queryTerms.split(";"); + LOGGER.info("Attributes : {} ", attributes); + + for (String attr : attributes) { + + String[] attributeQueryTerms = + attr.split("((?=>)|(?<=>)|(?=<)|(?<=<)|(?<==)|(?=!)|(?<=!)|(?==)|(?===))"); + LOGGER.info(Arrays.stream(attributeQueryTerms).collect(Collectors.toList())); + LOGGER.info(attributeQueryTerms.length); + if (attributeQueryTerms.length == 3) { + jsonOperator = attributeQueryTerms[1]; + jsonValue = attributeQueryTerms[2]; + json.put(JSON_OPERATOR, jsonOperator).put(JSON_VALUE, jsonValue); + } else if (attributeQueryTerms.length == 4) { + jsonOperator = attributeQueryTerms[1].concat(attributeQueryTerms[2]); + jsonValue = attributeQueryTerms[3]; + json.put(JSON_OPERATOR, jsonOperator).put(JSON_VALUE, jsonValue); } else { - if (specialCharFound && (Character.isLetter(c) || Character.isDigit(c))) { - json.put(Constants.JSON_OPERATOR, queryTerms.substring(startIndex, i)); - json.put(Constants.JSON_VALUE, queryTerms.substring(i)); - break; - } + throw new DxRuntimeException(failureCode(), INVALID_PARAM_VALUE_URN, failureMessage()); } + jsonAttribute = attributeQueryTerms[0]; + json.put(JSON_ATTRIBUTE, jsonAttribute); } - if (!allowedOperators.contains(json.getString(Constants.JSON_OPERATOR))) { - DxRuntimeException ex = - new DxRuntimeException( - BAD_REQUEST.getValue(), INVALID_ATTR_PARAM_URN, "Operator not allowed."); - this.context.fail(400, ex); - } + return json; } + + public int failureCode() { + return HttpStatusCode.BAD_REQUEST.getValue(); + } + + public String failureMessage() { + return INVALID_PARAM_VALUE_URN.getMessage(); + } } diff --git a/src/main/java/iudx/resource/server/apiserver/util/Constants.java b/src/main/java/iudx/resource/server/apiserver/util/Constants.java index 7ada10fc2..f2b0053a9 100644 --- a/src/main/java/iudx/resource/server/apiserver/util/Constants.java +++ b/src/main/java/iudx/resource/server/apiserver/util/Constants.java @@ -281,6 +281,7 @@ public class Constants { List.of("during", "between"); public static final Pattern VALIDATION_Q_ATTR_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{1,100}$"); + public static final Pattern VALIDATION_Q_VALUE_PATTERN = Pattern.compile("^[a-zA-Z0-9_.]{1,100}$"); // subscriptions queries public static final String CREATE_SUB_SQL = diff --git a/src/main/java/iudx/resource/server/apiserver/validation/types/QtypeValidator.java b/src/main/java/iudx/resource/server/apiserver/validation/types/QtypeValidator.java index 77b8c817e..1dd43aaad 100644 --- a/src/main/java/iudx/resource/server/apiserver/validation/types/QtypeValidator.java +++ b/src/main/java/iudx/resource/server/apiserver/validation/types/QtypeValidator.java @@ -24,18 +24,14 @@ public QtypeValidator(final String value, final boolean required) { this.required = required; } - private boolean isValidOperator(final String value) { - return VALIDATION_ALLOWED_OPERATORS.contains(value); - } - - public boolean isValidValue(final String value) { - try { - Float.parseFloat(value); - return true; - } catch (NumberFormatException ex) { - LOGGER.info("Passed value in q parameter is not float"); - throw new DxRuntimeException(failureCode(), INVALID_PARAM_VALUE_URN, failureMessage(value)); - } + private boolean isValidOperator(final String value, final boolean isNumericString) { + LOGGER.info(value); + LOGGER.info(value.equalsIgnoreCase("==")); + LOGGER.info(isNumericString); + LOGGER.info(VALIDATION_ALLOWED_OPERATORS.contains(value)); + return isNumericString + ? VALIDATION_ALLOWED_OPERATORS.contains(value) + : value.equalsIgnoreCase("=="); } private boolean isValidId(final JsonObject json) { @@ -48,42 +44,84 @@ private boolean isValidId(final JsonObject json) { } } - public boolean isValidAttributeValue(final String value) { - LOGGER.debug("value,{},{}", value, VALIDATION_Q_ATTR_PATTERN.matcher(value).matches()); + + public boolean isValidAttribute(final String value) { + LOGGER.info("value,{},{}", value, VALIDATION_Q_ATTR_PATTERN.matcher(value).matches()); return VALIDATION_Q_ATTR_PATTERN.matcher(value).matches(); } + public boolean isValidAttributeValue(final String value) { + LOGGER.info("value,{},{}", value, VALIDATION_Q_VALUE_PATTERN.matcher(value).matches()); + return VALIDATION_Q_VALUE_PATTERN.matcher(value).matches(); + } + + + JsonObject getQueryTerms(final String queryTerms) { JsonObject json = new JsonObject(); + String jsonOperator = ""; + String jsonValue = ""; + String jsonAttribute = ""; String[] attributes = queryTerms.split(";"); - LOGGER.debug(attributes); + LOGGER.info("Attributes : {} ", attributes); for (String attr : attributes) { String[] attributeQueryTerms = - attr.split("((?=>)|(?<=>)|(?=<)|(?<=<)|(?==)|(?<==)|(?=!)|(?<=!))"); - LOGGER.debug(Arrays.stream(attributeQueryTerms).collect(Collectors.toList())); - LOGGER.debug(attributeQueryTerms.length); + attr.split("((?=>)|(?<=>)|(?=<)|(?<=<)|(?<==)|(?=!)|(?<=!)|(?==)|(?===))"); + LOGGER.info(Arrays.stream(attributeQueryTerms).collect(Collectors.toList())); + LOGGER.info(attributeQueryTerms.length); if (attributeQueryTerms.length == 3) { - json.put(JSON_OPERATOR, attributeQueryTerms[1]).put(JSON_VALUE, attributeQueryTerms[2]); + jsonOperator = attributeQueryTerms[1]; + jsonValue = attributeQueryTerms[2]; + json.put(JSON_OPERATOR, jsonOperator).put(JSON_VALUE, jsonValue); } else if (attributeQueryTerms.length == 4) { - json.put(JSON_OPERATOR, attributeQueryTerms[1].concat(attributeQueryTerms[2])) - .put(JSON_VALUE, attributeQueryTerms[3]); + jsonOperator = attributeQueryTerms[1].concat(attributeQueryTerms[2]); + jsonValue = attributeQueryTerms[3]; + json.put(JSON_OPERATOR, jsonOperator).put(JSON_VALUE, jsonValue); } else { throw new DxRuntimeException(failureCode(), INVALID_PARAM_VALUE_URN, failureMessage(value)); } - json.put(JSON_ATTRIBUTE, attributeQueryTerms[0]); + jsonAttribute = attributeQueryTerms[0]; + json.put(JSON_ATTRIBUTE, jsonAttribute); + boolean isNumericString = isNumericString(jsonValue); + if (!isValidOperator(jsonOperator, isNumericString)) { + LOGGER.info("invalid operator : {} ", jsonOperator); + throw new DxRuntimeException( + failureCode(), INVALID_PARAM_VALUE_URN, failureMessage(jsonOperator)); + } + if (!isValidAttribute(jsonAttribute)) { + LOGGER.info("invalid attribute : {} ", jsonAttribute); + throw new DxRuntimeException( + failureCode(), INVALID_PARAM_VALUE_URN, failureMessage(jsonAttribute)); + } + if (!isValidAttributeValue(jsonValue)) { + LOGGER.info("invalid json value : {} ", jsonValue); + throw new DxRuntimeException( + failureCode(), INVALID_PARAM_VALUE_URN, failureMessage(jsonValue)); + } } - LOGGER.debug(json); - return json; } + private boolean isNumericString(String jsonValue) { + boolean isNumericString; + LOGGER.debug("Parsing value : {} as a float", jsonValue); + try { + Float.parseFloat(jsonValue); + isNumericString = true; + } catch (NumberFormatException ne) { + LOGGER.info("String based search"); + isNumericString = false; + } + return isNumericString; + } + @Override public boolean isValid() { - LOGGER.debug("value : " + value + " required : " + required); + LOGGER.info("value : " + value + " required : " + required); if (required && (value == null || value.isBlank())) { LOGGER.error("Validation error : null or blank value for required mandatory field"); throw new DxRuntimeException(failureCode(), INVALID_PARAM_VALUE_URN, failureMessage()); @@ -104,26 +142,11 @@ public boolean isValid() { try { qjson = getQueryTerms(value); } catch (Exception ex) { - LOGGER.error("Validation error : Operator not allowed."); - throw new DxRuntimeException(failureCode(), INVALID_PARAM_VALUE_URN, failureMessage(value)); - } - if (!isValidAttributeValue(qjson.getString(JSON_ATTRIBUTE))) { - LOGGER.error("Validation error : Not a valid attribute in <> query"); - throw new DxRuntimeException(failureCode(), INVALID_PARAM_VALUE_URN, failureMessage(value)); + LOGGER.error("Validation error : Operation not allowed."); + throw ex; } - if (!isValidOperator(qjson.getString(JSON_OPERATOR))) { - LOGGER.error("Validation error : Not a valid Operator in <> query"); - throw new DxRuntimeException(failureCode(), INVALID_PARAM_VALUE_URN, failureMessage(value)); - } - // if (!isValidAttributeValue(qJson.getString(JSON_VALUE))) { - // throw ValidationException.ValidationExceptionFactory - // .generateNotMatchValidationException("Not a valid attribute value in <> query"); - // } - if (!isValidId(qjson)) { - return false; - } - return true; + return isValidId(qjson); } @Override diff --git a/src/test/java/iudx/resource/server/apiserver/integrationTests/validations/validationAPIsIT.java b/src/test/java/iudx/resource/server/apiserver/integrationTests/validations/validationAPIsIT.java index eda2c041e..a5ee32646 100644 --- a/src/test/java/iudx/resource/server/apiserver/integrationTests/validations/validationAPIsIT.java +++ b/src/test/java/iudx/resource/server/apiserver/integrationTests/validations/validationAPIsIT.java @@ -361,7 +361,7 @@ void GetEntityInvOperator() { .then() .statusCode(400) .body("title", equalTo("Bad Request")) - .body("type", equalTo("urn:dx:rs:invalidAttributeParam")) + .body("type", equalTo("urn:dx:rs:invalidParameterValue")) .extract() .response(); } diff --git a/src/test/java/iudx/resource/server/apiserver/query/QueryMapperTest.java b/src/test/java/iudx/resource/server/apiserver/query/QueryMapperTest.java index decafba3f..53e666dbc 100644 --- a/src/test/java/iudx/resource/server/apiserver/query/QueryMapperTest.java +++ b/src/test/java/iudx/resource/server/apiserver/query/QueryMapperTest.java @@ -18,17 +18,17 @@ import static iudx.resource.server.apiserver.util.Constants.NGSILDQUERY_TIME; import static iudx.resource.server.apiserver.util.Constants.NGSILDQUERY_TIMEPROPERTY; import static iudx.resource.server.apiserver.util.Constants.NGSILDQUERY_TIMEREL; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.stream.Stream; + +import iudx.resource.server.apiserver.exceptions.DxRuntimeException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -50,10 +50,21 @@ @ExtendWith({VertxExtension.class,MockitoExtension.class}) public class QueryMapperTest { - private QueryMapper qm; @Mock RoutingContext context; + private QueryMapper qm; + static Stream invalidQTermsValues() { + // Add any valid value which will pass successfully. + return Stream.of( + Arguments.of("refrenceLevel+10", "Operator not allowed."), + Arguments.of("refrenceLevel/-10", "Operator not allowed."), + Arguments.of("refrenceLevel<+10", "Operator not allowed."), + Arguments.of("refrenceLevel>&10", "Operator not allowed."), + Arguments.of("refrenceLevel>+10", "Operator not allowed."), + Arguments.of("refrenceLevel+<10", "Operator not allowed.")); + + } @BeforeEach public void setup(Vertx vertx, VertxTestContext testContext) { @@ -136,7 +147,7 @@ public void testToJson4GeoQuery(Vertx vertx, VertxTestContext testContext) { assertFalse(json.containsKey(NGSILDQUERY_ENDTIME)); testContext.completeNow(); } - + @Test @Description("QueryMapper test for temporal queries(during)") public void testToJson4TemporalDuringQuery(Vertx vertx, VertxTestContext testContext) { @@ -176,7 +187,7 @@ public void testToJson4TemporalQuery(Vertx vertx, VertxTestContext testContext) assertTrue(json.containsKey(NGSILDQUERY_TIME)); testContext.completeNow(); } - + @Test @Description("QueryMapper test for temporal queries(Invalid time format)") public void testToJson4TemporalInvalidTimeQuery(Vertx vertx, VertxTestContext testContext) { @@ -213,27 +224,16 @@ public void testToJson4SimpleAttributeQuery(Vertx vertx, VertxTestContext testCo testContext.completeNow(); } - - static Stream invalidQTermsValues() { - // Add any valid value which will pass successfully. - return Stream.of( - Arguments.of("refrenceLevel+10", "Operator not allowed."), - Arguments.of("refrenceLevel/-10", "Operator not allowed."), - Arguments.of("refrenceLevel<>10", "Operator not allowed."), - Arguments.of("refrenceLevel><10", "Operator not allowed."), - Arguments.of("refrenceLevel>+10", "Operator not allowed."), - Arguments.of("refrenceLevel+<10", "Operator not allowed.")); - - } - @ParameterizedTest + @Disabled /* Request Validation Check done in QTypeValidator - Unit is not responsible for checking validity*/ @MethodSource("invalidQTermsValues") @Description("coordinates type parameter invalid values.") public void testInvalidQTermValue(String value, String result, Vertx vertx, VertxTestContext testContext) { - - qm.getQueryTerms(value); - verify(context,atLeast(1)).fail(anyInt(),any()); + + Exception exception = assertThrows(DxRuntimeException.class, () -> qm.getQueryTerms(value)); + String expectedMessage = exception.getMessage(); + System.out.println(expectedMessage); testContext.completeNow(); } diff --git a/src/test/java/iudx/resource/server/apiserver/validation/QtypeValidatorTest.java b/src/test/java/iudx/resource/server/apiserver/validation/QtypeValidatorTest.java index 7763404cd..702b24274 100644 --- a/src/test/java/iudx/resource/server/apiserver/validation/QtypeValidatorTest.java +++ b/src/test/java/iudx/resource/server/apiserver/validation/QtypeValidatorTest.java @@ -69,7 +69,7 @@ static Stream validValues() { return Stream.of( Arguments.of("referenceLevel>15.0", true), Arguments.of( - "id==iisc.ac.in/89a36273d77dac4cf38114fca1bbe64392547f86/rs.iudx.io/pune-env-flood/FWR055", + "license_plate==GJ05BU3663", true), Arguments.of(null, false)); } @@ -77,7 +77,7 @@ static Stream validValues() { @ParameterizedTest @MethodSource("validValues") @Description("success for valid q query") - public void testValidQValue(String value, boolean required, Vertx vertx, + public void testValidQValue(String value, boolean required, VertxTestContext testContext) { qTypeValidator = new QtypeValidator(value, required); assertTrue(qTypeValidator.isValid()); @@ -86,11 +86,11 @@ public void testValidQValue(String value, boolean required, Vertx vertx, @ParameterizedTest - @ValueSource(strings = {"","abcd","abcdeF","---"}) + @ValueSource(strings = {"","abcd$","abcdeF**","---"}) @DisplayName("Test isValidValue method : with invalid value") public void test_isValidValue_with_invalid_input(String input,VertxTestContext vertxTestContext) { - assertThrows(DxRuntimeException.class,()-> qTypeValidator.isValidValue(input)); + assertFalse(qTypeValidator.isValidAttributeValue(input)); vertxTestContext.completeNow(); } @@ -100,7 +100,7 @@ public void test_isValidValue_with_invalid_input(String input,VertxTestContext v @DisplayName("Test isValidValue method : with invalid value") public void test_isValidValue_with_valid_input(String input,VertxTestContext vertxTestContext) { - assertTrue( qTypeValidator.isValidValue(input)); + assertTrue( qTypeValidator.isValidAttributeValue(input)); vertxTestContext.completeNow(); } diff --git a/src/test/resources/IUDX-Resource-Server-Consumer-APIs-V5.5.0.postman_collection.json b/src/test/resources/IUDX-Resource-Server-Consumer-APIs-V5.5.0.postman_collection.json index ad650f5c8..c131bf76e 100644 --- a/src/test/resources/IUDX-Resource-Server-Consumer-APIs-V5.5.0.postman_collection.json +++ b/src/test/resources/IUDX-Resource-Server-Consumer-APIs-V5.5.0.postman_collection.json @@ -1,9 +1,9 @@ { "info": { - "_postman_id": "1e242c76-0800-47be-a499-5caad292c700", + "_postman_id": "ce6bc463-6553-42fd-aca1-07f1bfb67acc", "name": "IUDX-Resource-Server-Consumer-APIs-V5.0.0", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "20457128" + "_exporter_id": "17194681" }, "item": [ { @@ -9481,6 +9481,415 @@ } ] }, + { + "name": "String based Search", + "item": [ + { + "name": "200 (Success) String ==", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Test the response code", + "pm.test(\"response is 200 (OK)\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "// Test the response header", + "pm.test(\"Check response header\", function () {", + " pm.response.to.have.header(\"Content-Type\",\"application/json\");", + "});", + "", + "// Test the response", + "pm.test(\"Check response body\", function () { ", + " const body = pm.response.json();", + " pm.expect(body).to.have.property(\"title\", \"Success\");", + " const resultsjsonData = body.results[0];", + " pm.expect(body).to.have.property(\"results\");", + " pm.expect(resultsjsonData).to.have.property(\"id\");", + "});", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "token", + "value": "{{secureResourceToken}}", + "type": "text" + } + ], + "url": { + "raw": "{{base_url}}/{{basePath}}/entities?id=83c2e5c2-3574-4e11-9530-2b1fbdfce832&offset=0&limit=10&q=license_plate==GJ05BU3663", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{basePath}}", + "entities" + ], + "query": [ + { + "key": "id", + "value": "83c2e5c2-3574-4e11-9530-2b1fbdfce832" + }, + { + "key": "offset", + "value": "0" + }, + { + "key": "limit", + "value": "10" + }, + { + "key": "q", + "value": "license_plate==GJ05BU3663" + } + ] + } + }, + "response": [] + }, + { + "name": "200 (Success) Multiple String Attributes", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Test the response code", + "pm.test(\"response is 200 (OK)\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "// Test the response header", + "pm.test(\"Check response header\", function () {", + " pm.response.to.have.header(\"Content-Type\",\"application/json\");", + "});", + "", + "// Test the response", + "pm.test(\"Check response body\", function () { ", + " const body = pm.response.json();", + " pm.expect(body).to.have.property(\"title\", \"Success\");", + " const resultsjsonData = body.results[0];", + " pm.expect(body).to.have.property(\"results\");", + " pm.expect(resultsjsonData).to.have.property(\"id\");", + "});", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "token", + "value": "{{secureResourceToken}}", + "type": "text" + } + ], + "url": { + "raw": "{{base_url}}/{{basePath}}/entities?id=83c2e5c2-3574-4e11-9530-2b1fbdfce832&offset=0&limit=10&q=license_plate==GJ05BU3663;route_id==12U", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{basePath}}", + "entities" + ], + "query": [ + { + "key": "id", + "value": "83c2e5c2-3574-4e11-9530-2b1fbdfce832" + }, + { + "key": "offset", + "value": "0" + }, + { + "key": "limit", + "value": "10" + }, + { + "key": "q", + "value": "license_plate==GJ05BU3663;route_id==12U" + } + ] + } + }, + "response": [] + }, + { + "name": "204 (No Content) String ==", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Test the response code", + "pm.test(\"response is 200 (OK)\", function () {", + " pm.response.to.have.status(204);", + "});", + "", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "token", + "value": "{{secureResourceToken}}", + "type": "text" + } + ], + "url": { + "raw": "{{base_url}}/{{basePath}}/entities?id=83c2e5c2-3574-4e11-9530-2b1fbdfce832&offset=0&limit=10&q=license_plate==GJ05BU3661", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{basePath}}", + "entities" + ], + "query": [ + { + "key": "id", + "value": "83c2e5c2-3574-4e11-9530-2b1fbdfce832" + }, + { + "key": "offset", + "value": "0" + }, + { + "key": "limit", + "value": "10" + }, + { + "key": "q", + "value": "license_plate==GJ05BU3661" + } + ] + } + }, + "response": [] + }, + { + "name": "400 (Invalid Operator) String >", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Test the response code", + "pm.test(\"response is 400 (Bad Request Data)\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "// Test the response header", + "pm.test(\"Check response header\", function () {", + " pm.response.to.have.header(\"Content-Type\",\"application/json\");", + "});", + "", + "// Test the response", + "pm.test(\"Check response body\", function () { ", + " const body = pm.response.json();", + " pm.expect(body).to.have.property(\"type\", \"urn:dx:rs:invalidParameterValue\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "token", + "value": "{{secureResourceToken}}", + "type": "text" + } + ], + "url": { + "raw": "{{base_url}}/{{basePath}}/entities?id=83c2e5c2-3574-4e11-9530-2b1fbdfce832&offset=0&limit=10&q=license_plate>GJ05BU3663", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{basePath}}", + "entities" + ], + "query": [ + { + "key": "id", + "value": "83c2e5c2-3574-4e11-9530-2b1fbdfce832" + }, + { + "key": "offset", + "value": "0" + }, + { + "key": "limit", + "value": "10" + }, + { + "key": "q", + "value": "license_plate>GJ05BU3663" + } + ] + } + }, + "response": [] + }, + { + "name": "400 (Invalid Value) String > Copy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Test the response code", + "pm.test(\"response is 400 (Bad Request Data)\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "// Test the response header", + "pm.test(\"Check response header\", function () {", + " pm.response.to.have.header(\"Content-Type\",\"application/json\");", + "});", + "", + "// Test the response", + "pm.test(\"Check response body\", function () { ", + " const body = pm.response.json();", + " pm.expect(body).to.have.property(\"type\", \"urn:dx:rs:invalidParameterValue\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "token", + "value": "{{secureResourceToken}}", + "type": "text" + } + ], + "url": { + "raw": "{{base_url}}/{{basePath}}/entities?id=83c2e5c2-3574-4e11-9530-2b1fbdfce832&offset=0&limit=10&q=license_plate==!@%23$^%26*()-=_+{}:\"<>?[],./'", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{basePath}}", + "entities" + ], + "query": [ + { + "key": "id", + "value": "83c2e5c2-3574-4e11-9530-2b1fbdfce832" + }, + { + "key": "offset", + "value": "0" + }, + { + "key": "limit", + "value": "10" + }, + { + "key": "q", + "value": "license_plate==!@%23$^%26*()-=_+{}:\"<>?[],./'" + } + ] + } + }, + "response": [] + }, + { + "name": "400 (Invalid Value) String > Copy 2", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Test the response code", + "pm.test(\"response is 400 (Bad Request Data)\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "// Test the response header", + "pm.test(\"Check response header\", function () {", + " pm.response.to.have.header(\"Content-Type\",\"application/json\");", + "});", + "", + "// Test the response", + "pm.test(\"Check response body\", function () { ", + " const body = pm.response.json();", + " pm.expect(body).to.have.property(\"type\", \"urn:dx:rs:invalidParameterValue\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "token", + "value": "{{secureResourceToken}}", + "type": "text" + } + ], + "url": { + "raw": "{{base_url}}/{{basePath}}/entities?id=83c2e5c2-3574-4e11-9530-2b1fbdfce832&offset=0&limit=10&q=license_!@%23$^==GJ05BU3663", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{basePath}}", + "entities" + ], + "query": [ + { + "key": "id", + "value": "83c2e5c2-3574-4e11-9530-2b1fbdfce832" + }, + { + "key": "offset", + "value": "0" + }, + { + "key": "limit", + "value": "10" + }, + { + "key": "q", + "value": "license_!@#$^==GJ05BU3663" + } + ] + } + }, + "response": [] + } + ] + }, { "name": "Complex Search", "item": [ @@ -9587,6 +9996,115 @@ }, "response": [] }, + { + "name": "200 (success) - Search - circle geom + temporal before + string search", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Test the response code", + "pm.test(\"response is 200 (OK)\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "// Test the response header", + "pm.test(\"Check response header\", function () {", + " pm.response.to.have.header(\"Content-Type\",\"application/json\");", + "});", + "", + "// Test the response", + "pm.test(\"Check response body\", function () { ", + " const body = pm.response.json();", + " pm.expect(body).to.have.property(\"title\", \"Success\");", + " const resultsjsonData = body.results[0];", + " pm.expect(resultsjsonData).to.have.property(\"id\");", + " pm.expect(resultsjsonData).to.have.property(\"speed\");", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "token", + "type": "text", + "value": "{{secureResourceToken}}" + } + ], + "url": { + "raw": "{{base_url}}/{{basePath}}/temporal/entities?id=83c2e5c2-3574-4e11-9530-2b1fbdfce832&geoproperty=location&georel=near;maxDistance=10&geometry=Point&coordinates=[21.178,72.834]&timerel=before&time=2020-10-19T14:00:00Z", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{basePath}}", + "temporal", + "entities" + ], + "query": [ + { + "key": "id", + "value": "83c2e5c2-3574-4e11-9530-2b1fbdfce832" + }, + { + "key": "geoproperty", + "value": "location" + }, + { + "key": "georel", + "value": "near;maxDistance=10" + }, + { + "key": "geometry", + "value": "Point" + }, + { + "key": "coordinates", + "value": "[21.178,72.834]" + }, + { + "key": "timerel", + "value": "before" + }, + { + "key": "time", + "value": "2020-10-19T14:00:00Z" + }, + { + "key": "attrs", + "value": "id,location,speed", + "disabled": true + }, + { + "key": "options", + "value": "count", + "disabled": true + }, + { + "key": "offset", + "value": "0", + "disabled": true + }, + { + "key": "limit", + "value": "400", + "disabled": true + }, + { + "key": "q", + "value": "license_plate==GJ05BU3663", + "disabled": true + } + ] + } + }, + "response": [] + }, { "name": "200 (success) - Search - circle geom + temporal before + response filter with optional encryption", "event": [