Skip to content

Commit

Permalink
Merge branch 'master' into feature/icat-to-panosc-data-model-conversi…
Browse files Browse the repository at this point in the history
…on-#265
  • Loading branch information
MRichards99 committed Jan 31, 2022
2 parents 358ac22 + 63f4f17 commit f17038d
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 13 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

<!--next-version-placeholder-->

## v3.4.0 (2022-01-31)
### Feature
* Implement `regexp` operator #297 ([`bf3fe0e`](https://github.com/ral-facilities/datagateway-api/commit/bf3fe0ef2ac582d55dbd881edf6a81a93625ce91))
* Implement `neq` operator #297 ([`9094bbb`](https://github.com/ral-facilities/datagateway-api/commit/9094bbb894ead20a53fadfd0e24b264af29548b9))
* Implement `nin` operator #297 ([`00dbba5`](https://github.com/ral-facilities/datagateway-api/commit/00dbba525d5cd86cb5577f3b1621a7042cdd2fa0))
* Implement `inq` operator #297 ([`fc1cf19`](https://github.com/ral-facilities/datagateway-api/commit/fc1cf194454a4da60652b1f68df278c4624ddc11))
* Implement `between` operator #297 ([`4736888`](https://github.com/ral-facilities/datagateway-api/commit/4736888bf76cda0dbc00f997443ed565f0f5e760))

## v3.3.0 (2022-01-31)
### Feature
* Add function to get PaNOSC to ICAT mapping for where filter #260 ([`34b1d81`](https://github.com/ral-facilities/datagateway-api/commit/34b1d819482aa3efdb4f8da321125d3e40d76617))
Expand Down
11 changes: 8 additions & 3 deletions datagateway_api/src/common/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ def __init__(self, field, value, operation):
self.value = value
self.operation = operation

if self.operation == "in":
if self.operation in ["in", "nin", "inq", "between"]:
if not isinstance(self.value, list):
raise BadRequestError(
"When using the 'in' operation for a WHERE filter, the values must"
" be in a list format e.g. [1, 2, 3]",
f"When using the {self.operation} operation for a WHERE filter, the"
f" values must be in a list format e.g. [1, 2]",
)
if self.operation == "between" and len(self.value) != 2:
raise BadRequestError(
"When using the 'between' operation for a WHERE filter, the list"
"must contain two values e.g. [1, 2]",
)


Expand Down
26 changes: 22 additions & 4 deletions datagateway_api/src/datagateway_api/icat/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def create_filter(self):
log.info("Creating condition for ICAT where filter")
if self.operation == "eq":
where_filter = self.create_condition(self.field, "=", self.value)
elif self.operation == "ne":
elif self.operation in ["ne", "neq"]:
where_filter = self.create_condition(self.field, "!=", self.value)
elif self.operation == "like":
where_filter = self.create_condition(self.field, "like", f"%{self.value}%")
Expand All @@ -75,7 +75,7 @@ def create_filter(self):
where_filter = self.create_condition(self.field, ">", self.value)
elif self.operation == "gte":
where_filter = self.create_condition(self.field, ">=", self.value)
elif self.operation == "in":
elif self.operation in ["in", "inq"]:
# Convert self.value into a string with brackets equivalent to tuple format.
# Cannot convert straight to tuple as single element tuples contain a
# trailing comma which Python ICAT/JPQL doesn't accept
Expand All @@ -88,6 +88,25 @@ def create_filter(self):
self.value = "(NULL)"

where_filter = self.create_condition(self.field, "in", self.value)
elif self.operation == "nin":
# Convert self.value into a string with brackets equivalent to tuple format.
# Cannot convert straight to tuple as single element tuples contain a
# trailing comma which Python ICAT/JPQL doesn't accept
self.value = str(self.value).replace("[", "(").replace("]", ")")

# DataGateway Search can send requests with blank lists. Adding NULL to the
# filter prevents the API from returning a 500. An empty list will be
# returned instead, equivalent to the DB backend
if self.value == "()":
self.value = "(NULL)"

where_filter = self.create_condition(self.field, "not in", self.value)
elif self.operation == "between":
where_filter = self.create_condition(
self.field, "between", f"'{self.value[0]}' and '{self.value[1]}'",
)
elif self.operation == "regexp":
where_filter = self.create_condition(self.field, "regexp", self.value)
else:
raise FilterError(f"Bad operation given to where filter: {self.operation}")

Expand Down Expand Up @@ -116,8 +135,7 @@ def create_condition(attribute_name, operator, value):
# distinct filter is used in a request
jpql_value = (
f"{value}"
if operator == "in"
or operator == "!="
if operator in ("in", "not in", "!=", "between")
or str(value).startswith("UPPER")
or "o." in str(value)
else f"'{value}'"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "datagateway-api"
version = "3.3.0"
version = "3.4.0"
description = "ICAT API to interface with the DataGateway"
license = "Apache-2.0"
readme = "README.md"
Expand Down
39 changes: 34 additions & 5 deletions test/datagateway_api/icat/filters/test_where_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class TestICATWhereFilter:
"operation, value, expected_condition_value",
[
pytest.param("eq", 5, ["%s = '5'"], id="equal"),
pytest.param("ne", 5, ["%s != 5"], id="not equal"),
pytest.param("ne", 5, ["%s != 5"], id="not equal (ne)"),
pytest.param("neq", 5, ["%s != 5"], id="not equal (neq)"),
pytest.param("like", 5, ["%s like '%%5%%'"], id="like"),
pytest.param("ilike", 5, ["UPPER(%s) like UPPER('%%5%%')"], id="ilike"),
pytest.param("nlike", 5, ["%s not like '%%5%%'"], id="not like"),
Expand All @@ -21,8 +22,20 @@ class TestICATWhereFilter:
pytest.param("lte", 5, ["%s <= '5'"], id="less than or equal"),
pytest.param("gt", 5, ["%s > '5'"], id="greater than"),
pytest.param("gte", 5, ["%s >= '5'"], id="greater than or equal"),
pytest.param("in", [1, 2, 3, 4], ["%s in (1, 2, 3, 4)"], id="in a list"),
pytest.param("in", [], ["%s in (NULL)"], id="empty list"),
pytest.param(
"in", [1, 2, 3, 4], ["%s in (1, 2, 3, 4)"], id="in a list (in)",
),
pytest.param("in", [], ["%s in (NULL)"], id="in empty list (in)"),
pytest.param(
"inq", [1, 2, 3, 4], ["%s in (1, 2, 3, 4)"], id="in a list (inq)",
),
pytest.param("inq", [], ["%s in (NULL)"], id="in empty list (inq)"),
pytest.param(
"nin", [1, 2, 3, 4], ["%s not in (1, 2, 3, 4)"], id="not in a list",
),
pytest.param("nin", [], ["%s not in (NULL)"], id="not in empty list"),
pytest.param("between", [1, 2], ["%s between '1' and '2'"], id="between"),
pytest.param("regexp", "^Test", ["%s regexp '^Test'"], id="regexp"),
],
)
def test_valid_operations(
Expand All @@ -33,9 +46,25 @@ def test_valid_operations(

assert icat_query.conditions == {"id": expected_condition_value}

def test_invalid_in_operation(self, icat_query):
@pytest.mark.parametrize(
"operation, value",
[
pytest.param("in", "1, 2, 3, 4, 5", id="in a list (in)"),
pytest.param("inq", "1, 2, 3, 4, 5", id="in a list (inq)"),
pytest.param("nin", "1, 2, 3, 4, 5", id="nin"),
pytest.param("between", "1, 2, 3, 4, 5", id="between - string value"),
pytest.param("between", [], id="between - empty list"),
pytest.param(
"between", [1], id="between - list with less than two elements",
),
pytest.param(
"between", [1, 2, 3], id="between - list with more than two elements",
),
],
)
def test_invalid_operations_raise_bad_request_error(self, operation, value):
with pytest.raises(BadRequestError):
PythonICATWhereFilter("id", "1, 2, 3, 4, 5", "in")
PythonICATWhereFilter("id", value, operation)

def test_invalid_operation(self, icat_query):
test_filter = PythonICATWhereFilter("id", 10, "non")
Expand Down

0 comments on commit f17038d

Please sign in to comment.