Skip to content

Commit de821d4

Browse files
committed
Add filter endpoint tests, switch to unittest provided asserts, make pylint happier
1 parent ee92239 commit de821d4

20 files changed

+1177
-860
lines changed

.pylintrc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
extension-pkg-whitelist=
77

88
# Specify a score threshold to be exceeded before program exits with error.
9-
fail-under=10.0
9+
fail-under=10
1010

1111
# Add files or directories to the blacklist. They should be base names, not
1212
# paths.
@@ -589,6 +589,3 @@ valid-metaclass-classmethod-first-arg=cls
589589
# "BaseException, Exception".
590590
overgeneral-exceptions=BaseException,
591591
Exception
592-
593-
[MESSAGES CONTROL]
594-
disable = C0330, C0326

gramps_webapi/api/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ def register_endpt(resource: Type[Resource], url: str, name: str):
6868
register_endpt(BookmarkResource, "/bookmarks/<string:namespace>", "bookmark")
6969
register_endpt(BookmarksResource, "/bookmarks/", "bookmarks")
7070
# Filter
71-
register_endpt(FilterResource, "/filters/<string:namespace>", "filter")
71+
register_endpt(FilterResource, "/filters/<string:namespace>/<string:name>", "filter")
72+
register_endpt(FilterResource, "/filters/<string:namespace>", "filters")
7273
# Translate
7374
register_endpt(TranslationResource, "/translations/<string:isocode>", "translation")
7475
register_endpt(TranslationsResource, "/translations/", "translations")

gramps_webapi/api/resources/filters.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from flask import Response, abort
88
from gramps.gen.db.base import DbReadBase
99
from gramps.gen.filters import GenericFilter
10-
from marshmallow import Schema, ValidationError, fields
11-
from webargs import validate
10+
from marshmallow import Schema
11+
from webargs import ValidationError, fields, validate
1212
from webargs.flaskparser import use_args
1313

1414
from ...const import GRAMPS_NAMESPACES
@@ -34,8 +34,9 @@ def get_filter_rules(args: Dict[str, str], namespace: str) -> List[Dict]:
3434
rule_list = []
3535
for rule_class in _RULES_LOOKUP[namespace]:
3636
add_rule = True
37-
if args.get("rule") and args["rule"] != rule_class.__name__:
38-
add_rule = False
37+
if "rules" in args and args["rules"]:
38+
if rule_class.__name__ not in args["rules"]:
39+
add_rule = False
3940
if add_rule:
4041
rule_list.append(
4142
{
@@ -46,6 +47,8 @@ def get_filter_rules(args: Dict[str, str], namespace: str) -> List[Dict]:
4647
"rule": rule_class.__name__,
4748
}
4849
)
50+
if "rules" in args and len(args["rules"]) != len(rule_list):
51+
abort(404)
4952
return rule_list
5053

5154

@@ -55,8 +58,9 @@ def get_custom_filters(args: Dict[str, str], namespace: str) -> List[Dict]:
5558
filters.reload_custom_filters()
5659
for filter_class in filters.CustomFilters.get_filters(namespace):
5760
add_filter = True
58-
if args.get("filter") and args["filter"] != filter_class.get_name():
59-
add_filter = False
61+
if "filters" in args and args["filters"]:
62+
if filter_class.get_name() not in args["filters"]:
63+
add_filter = False
6064
if add_filter:
6165
filter_list.append(
6266
{
@@ -74,6 +78,8 @@ def get_custom_filters(args: Dict[str, str], namespace: str) -> List[Dict]:
7478
],
7579
}
7680
)
81+
if "filters" in args and len(args["filters"]) != len(filter_list):
82+
abort(404)
7783
return filter_list
7884

7985

@@ -113,7 +119,7 @@ def apply_filter(db_handle: DbReadBase, args: Dict, namespace: str) -> List[Hand
113119
for filter_class in filters.CustomFilters.get_filters(namespace):
114120
if args["filter"] == filter_class.get_name():
115121
return filter_class.apply(db_handle)
116-
abort(400)
122+
abort(404)
117123

118124
try:
119125
filter_parms = FilterSchema().load(json.loads(args["rules"]))
@@ -143,21 +149,31 @@ class FilterSchema(Schema):
143149
validate=validate.OneOf(["and", "or", "xor", "one"]),
144150
)
145151
invert = fields.Boolean(required=False, missing=False)
146-
rules = fields.List(fields.Nested(RuleSchema))
152+
rules = fields.List(
153+
fields.Nested(RuleSchema), required=True, validate=validate.Length(min=1)
154+
)
147155

148156

149157
class CustomFilterSchema(FilterSchema):
150158
"""Structure for a custom filter."""
151159

152160
name = fields.Str(required=True, validate=validate.Length(min=1))
153161
comment = fields.Str(required=False)
162+
rules = fields.List(
163+
fields.Nested(RuleSchema), required=True, validate=validate.Length(min=1)
164+
)
154165

155166

156167
class FilterResource(ProtectedResource, GrampsJSONEncoder):
157168
"""Filter resource."""
158169

159170
@use_args(
160-
{"filter": fields.Str(), "rule": fields.Str()},
171+
{
172+
"filters": fields.DelimitedList(
173+
fields.Str(validate=validate.Length(min=1))
174+
),
175+
"rules": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))),
176+
},
161177
location="query",
162178
)
163179
def get(self, args: Dict[str, str], namespace: str) -> Response:
@@ -168,10 +184,10 @@ def get(self, args: Dict[str, str], namespace: str) -> Response:
168184
abort(404)
169185

170186
rule_list = get_filter_rules(args, namespace)
171-
if args.get("rule") and not args.get("filter"):
187+
if "rules" in args and "filters" not in args:
172188
return self.response(200, {"rules": rule_list})
173189
filter_list = get_custom_filters(args, namespace)
174-
if args.get("filter") and not args.get("rule"):
190+
if "filters" in args and "rules" not in args:
175191
return self.response(200, {"filters": filter_list})
176192
return self.response(200, {"filters": filter_list, "rules": rule_list})
177193

@@ -214,12 +230,11 @@ def put(self, args: Dict, namespace: str) -> Response:
214230

215231
@use_args(
216232
{
217-
"name": fields.Str(required=True),
218-
"force": fields.Boolean(required=False, missing=False),
233+
"force": fields.Str(validate=validate.Length(equal=0)),
219234
},
220-
location="json",
235+
location="query",
221236
)
222-
def delete(self, args: Dict, namespace: str) -> Response:
237+
def delete(self, args: Dict, namespace: str, name: str) -> Response:
223238
"""Delete a custom filter."""
224239
try:
225240
namespace = GRAMPS_NAMESPACES[namespace]
@@ -229,17 +244,15 @@ def delete(self, args: Dict, namespace: str) -> Response:
229244
filters.reload_custom_filters()
230245
custom_filters = filters.CustomFilters.get_filters(namespace)
231246
for custom_filter in custom_filters:
232-
if args["name"] == custom_filter.get_name():
247+
if name == custom_filter.get_name():
233248
filter_set = set()
234249
self._find_dependent_filters(namespace, custom_filter, filter_set)
235250
if len(filter_set) > 1:
236251
if "force" not in args or not args["force"]:
237252
abort(405)
238253
list(map(custom_filters.remove, filter_set))
239254
filters.CustomFilters.save()
240-
return self.response(
241-
200, {"message": "Deleted filter: " + args["name"]}
242-
)
255+
return self.response(200, {"message": "Deleted filter: " + name})
243256
return abort(404)
244257

245258
def _find_dependent_filters(

gramps_webapi/data/apispec.yaml

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,16 +1752,16 @@ paths:
17521752
required: true
17531753
type: string
17541754
description: "The namespace or category for the filters."
1755-
- name: rule
1755+
- name: rules
17561756
in: query
17571757
required: false
17581758
type: string
1759-
description: "The name of a specific filter rule to be returned."
1760-
- name: filter
1759+
description: "A comma delimited list of specific filter rules to be returned."
1760+
- name: filters
17611761
in: query
17621762
required: false
17631763
type: string
1764-
description: "The name of a specific custom filter to be returned."
1764+
description: "A comma delimited list of specific custom filters to be returned."
17651765
responses:
17661766
200:
17671767
description: "OK: Successful operation."
@@ -1835,6 +1835,7 @@ paths:
18351835
403:
18361836
description: "Forbidden: Bad credentials, authentication failed."
18371837

1838+
/filters/{namespace}/{name}:
18381839
delete:
18391840
tags:
18401841
- filters
@@ -1848,12 +1849,16 @@ paths:
18481849
required: true
18491850
type: string
18501851
description: "The namespace or category for the custom filter."
1851-
- name: filter
1852-
in: body
1852+
- name: name
1853+
in: path
18531854
required: true
1854-
description: "The custom filter to delete."
1855-
schema:
1856-
$ref: "#/definitions/CustomFilterObject"
1855+
type: string
1856+
description: "The name of the custom filter."
1857+
- name: force
1858+
in: query
1859+
required: false
1860+
type: string
1861+
description: "Force delete custom filter and all filters that depend upon it."
18571862
responses:
18581863
200:
18591864
description: "OK: Successful operation."
@@ -3972,24 +3977,6 @@ definitions:
39723977
items:
39733978
$ref: "#/definitions/FilterRule"
39743979

3975-
##############################################################################
3976-
# Model - CustomFilterObject
3977-
##############################################################################
3978-
3979-
CustomFilterObject:
3980-
type: object
3981-
required:
3982-
- name
3983-
properties:
3984-
name:
3985-
type: string
3986-
description: "Name of the custom rule to delete."
3987-
example: "MyTestRule"
3988-
force:
3989-
type: boolean
3990-
description: "Indicator to approve deletion of all dependent filters if any exist."
3991-
example: true
3992-
39933980
##############################################################################
39943981
# Model - Translations
39953982
##############################################################################

tests/test_endpoints/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
import gramps_webapi.app
1414
from gramps_webapi.app import create_app
1515
from gramps_webapi.const import ENV_CONFIG_FILE, TEST_EXAMPLE_GRAMPS_CONFIG
16-
17-
from .. import TEST_GRAMPSHOME, ExampleDbSQLite
16+
from tests import TEST_GRAMPSHOME, ExampleDbSQLite
1817

1918

2019
def get_object_count(gramps_object):

0 commit comments

Comments
 (0)