Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

validate legal/search endpoint filters #6088

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions tests/integration/test_ao_elasticsearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,22 @@ def test_ao_requestor_filter(self):
assert all(requestor in doc["requestor_names"] for doc in response)
self.check_incorrect_values({"ao_requestor": "Incorrect"}, False)

types = [5, 15]
requstor_type_full = [REQUESTOR_TYPES[5], REQUESTOR_TYPES[15]]
types = ["5", "15"]
requstor_type_full = [REQUESTOR_TYPES["5"], REQUESTOR_TYPES["15"]]
response = self._results_ao(api.url_for(UniversalSearch, ao_requestor_type=types))
# logging.info(response)

assert all(any(requestor in requstor_type_full
for requestor in doc["requestor_types"])
for doc in response)

type = 15
type = "15"
response = self._results_ao(api.url_for(UniversalSearch, ao_requestor_type=type))
# logging.info(response)

assert all(REQUESTOR_TYPES[type] in doc["requestor_types"] for doc in response)

self.check_incorrect_values({"ao_requestor_type": 22}, True)
self.check_incorrect_values({"ao_requestor_type": "22"}, True)

def test_ao_entity_name_filter(self):
entity_name = "Francis Beaver"
Expand Down
19 changes: 10 additions & 9 deletions webservices/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ def make_seek_args(field=fields.Int, description=None):
'from_hit': fields.Int(required=False, description=docs.FROM_HIT),
'hits_returned': fields.Int(required=False, description=docs.HITS_RETURNED),
'type': fields.Str(
validate=validate.OneOf(["admin_fines", "adrs", "advisory_opinions",
"murs", "statutes"]),
validate=validate.OneOf(['', 'admin_fines', 'adrs', 'advisory_opinions',
'murs', 'statutes']),
description=docs.LEGAL_DOC_TYPE),

'ao_no': fields.List(IStr, required=False, description=docs.AO_NUMBER),
Expand All @@ -368,9 +368,10 @@ def make_seek_args(field=fields.Int, description=None):
'ao_is_pending': fields.Bool(description=docs.AO_IS_PENDING),
'ao_status': fields.Str(description=docs.AO_STATUS),
'ao_requestor': fields.Str(description=docs.AO_REQUESTOR),
'ao_requestor_type': fields.List(
fields.Integer(validate=validate.OneOf(range(1, 17))),
description=docs.AO_REQUESTOR_TYPE),
'ao_requestor_type': fields.List(IStr(
validate=validate.OneOf(['', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11',
'12', '13', '14', '15', '16'])),
description=docs.AO_REQUESTOR_TYPE),
'ao_regulatory_citation': fields.List(IStr, required=False, description=docs.REGULATORY_CITATION),
'ao_statutory_citation': fields.List(IStr, required=False, description=docs.STATUTORY_CITATION),
'ao_citation_require_all': fields.Bool(description=docs.CITATION_REQUIRE_ALL),
Expand All @@ -383,12 +384,12 @@ def make_seek_args(field=fields.Int, description=None):
'primary_subject_id': fields.List(IStr(
validate=validate.OneOf(
['', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11',
'12', '13', '14', '15', '16', '17', '18', '19', '20'])),
'12', '13', '14', '15', '16', '17', '18', '19', '20'])),
description=docs.PRIMARY_SUBJECT_DESCRIPTION),
'secondary_subject_id': fields.List(IStr(
validate=validate.OneOf(
['', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11',
'12', '13', '14', '15', '16', '17', '18'])),
'12', '13', '14', '15', '16', '17', '18'])),
description=docs.SECONDARY_SUBJECT_DESCRIPTION),
'case_max_open_date': Date(required=False, description=docs.CASE_MAX_OPEN_DATE),
'case_min_close_date': Date(required=False, description=docs.CASE_MIN_CLOSE_DATE),
Expand All @@ -405,7 +406,7 @@ def make_seek_args(field=fields.Int, description=None):
description=docs.CASE_DOCUMENT_CATEGORY_DESCRIPTION),

'mur_type': fields.Str(
required=False, validate=validate.OneOf(["archived", "current"]), description=docs.MUR_TYPE),
required=False, validate=validate.OneOf(['', 'archived', 'current']), description=docs.MUR_TYPE),
'mur_disposition_category_id': fields.List(IStr(
validate=validate.OneOf([
'', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
Expand Down Expand Up @@ -433,7 +434,7 @@ def make_seek_args(field=fields.Int, description=None):

citation = {
'doc_type': fields.Str(
required=False, validate=validate.OneOf(["adrs", "advisory_opinions", "murs"]),
required=False, validate=validate.OneOf(['', 'adrs', 'advisory_opinions', 'murs']),
description=docs.CITATION_DOC_TYPE
)
}
Expand Down
17 changes: 17 additions & 0 deletions webservices/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2150,6 +2150,23 @@ def add_ytd(var):

AO_REQUESTOR_TYPE = '''
Code of the advisory opinion requestor type.
Select one or more codes to filter by advisory opinion requestor type:\n\
- 1 - Federal candidate/candidate committee/officeholder\n\
- 2 - Publicly funded candidates/committees\n\
- 3 - Party committee, national\n\
- 4 - Party committee, state or local\n\
- 5 - Nonconnected political committee\n\
- 6 - Separate segregated fun \n\
- 7 - Labor Organization\n\
- 8 - Trade Association\n\
- 9 - Membership Organization, Cooperative, Corporation W/O Capital Stocks\n\
- 10 - Corporation (including LLCs electing corporate status)\n\
- 11 - Partnership (including LLCs electing partnership status)\n\
- 12 - Governmental entity \n\
- 13 - Research/Public Interest/Educational Institution\n\
- 14 - Law Firm\n\
- 15 - Individual\n\
- 16 - Other\n\
'''

CITATION_DOC_TYPE = '''
Expand Down
11 changes: 11 additions & 0 deletions webservices/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,14 @@ def get_cycle(kwargs):
)
return kwargs['cycle'][0]
return kwargs['cycle']


def validate_multiselect_filter(filter, valid_values):
valid_results = []

# Validate each value in filter
for value in filter:
if isinstance(value, str) and ' ' not in value:
if value in valid_values:
valid_results.append(value)
return valid_results
80 changes: 55 additions & 25 deletions webservices/resources/legal.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from flask_apispec import doc
from webservices import docs
from webservices import args
from webservices import filters
from webservices.utils import (
create_es_client,
Resource,
Expand Down Expand Up @@ -49,22 +50,22 @@
ACCEPTED_DATE_FORMATS = "strict_date_optional_time_nanos||MM/dd/yyyy||M/d/yyyy||MM/d/yyyy||M/dd/yyyy"

REQUESTOR_TYPES = {
1: "Federal candidate/candidate committee/officeholder",
2: "Publicly funded candidates/committees",
3: "Party committee, national",
4: "Party committee, state or local",
5: "Nonconnected political committee",
6: "Separate segregated fund",
7: "Labor Organization",
8: "Trade Association",
9: "Membership Organization, Cooperative, Corporation W/O Capital Stock",
10: "Corporation (including LLCs electing corporate status)",
11: "Partnership (including LLCs electing partnership status)",
12: "Governmental entity",
13: "Research/Public Interest/Educational Institution",
14: "Law Firm",
15: "Individual",
16: "Other",
"1": "Federal candidate/candidate committee/officeholder",
"2": "Publicly funded candidates/committees",
"3": "Party committee, national",
"4": "Party committee, state or local",
"5": "Nonconnected political committee",
"6": "Separate segregated fund",
"7": "Labor Organization",
"8": "Trade Association",
"9": "Membership Organization, Cooperative, Corporation W/O Capital Stock",
"10": "Corporation (including LLCs electing corporate status)",
"11": "Partnership (including LLCs electing partnership status)",
"12": "Governmental entity",
"13": "Research/Public Interest/Educational Institution",
"14": "Law Firm",
"15": "Individual",
"16": "Other",
}

# endpoint path: /legal/docs/<doc_type>/<no>
Expand Down Expand Up @@ -128,8 +129,8 @@ def get(self, q="", from_hit=0, hits_returned=20, **kwargs):
"adrs": case_query_builder,
"admin_fines": case_query_builder,
}

if kwargs.get("type", "all") == "all":
# Get the type from kwargs, defaulting to '' if not present
if kwargs.get("type", "") == "":
doc_types = ALL_DOCUMENT_TYPES
else:
doc_types = [kwargs.get("type")]
Expand Down Expand Up @@ -235,13 +236,33 @@ def case_query_builder(q, type_, from_hit, hits_returned, **kwargs):
if check_filter_exists(kwargs, "case_no"):
must_clauses.append(Q("terms", no=kwargs.get("case_no")))

if kwargs.get("primary_subject_id") and '' not in kwargs.get("primary_subject_id"):
# Get 'primary_subject_id' from kwargs
primary_subject_id = kwargs.get("primary_subject_id", [])
primary_subject_id_valid_values = ['1', '2', '3', '4', '5', '6', '7', '8', '9',
'10', '11', '12', '13', '14', '15', '16',
'17', '18', '19', '20']

# Validate 'primary_subject_id filter'
valid_primary_subject_id = filters.validate_multiselect_filter(
primary_subject_id, primary_subject_id_valid_values)

if valid_primary_subject_id:
must_clauses.append(Q("nested", path="subjects",
query=Q("terms", subjects__primary_subject_id=kwargs.get("primary_subject_id"))))
query=Q("terms", subjects__primary_subject_id=valid_primary_subject_id)))

# Get 'secondary_subject_id' from kwargs
secondary_subject_id = kwargs.get("secondary_subject_id", [])
secondary_subject_id_valid_values = ['1', '2', '3', '4', '5', '6', '7', '8', '9',
'10', '11', '12', '13', '14', '15', '16',
'17', '18']

if kwargs.get("secondary_subject_id") and '' not in kwargs.get("secondary_subject_id"):
# Validate 'secondary_subject_id filter'
valid_secondary_subject_id = filters.validate_multiselect_filter(
secondary_subject_id, secondary_subject_id_valid_values)

if valid_secondary_subject_id:
must_clauses.append(Q("nested", path="subjects",
query=Q("terms", subjects__secondary_subject_id=kwargs.get("secondary_subject_id"))))
query=Q("terms", subjects__secondary_subject_id=valid_secondary_subject_id)))

if kwargs.get("case_respondents"):
must_clauses.append(Q("simple_query_string",
Expand Down Expand Up @@ -437,7 +458,7 @@ def apply_af_specific_query_params(query, **kwargs):
def apply_mur_specific_query_params(query, **kwargs):
must_clauses = []

if kwargs.get("mur_type"):
if check_filter_exists(kwargs, "mur_type"):
must_clauses.append(Q("match", mur_type=kwargs.get("mur_type")))

if kwargs.get("case_election_cycles"):
Expand Down Expand Up @@ -736,13 +757,22 @@ def apply_ao_specific_query_params(query, **kwargs):
else:
must_clauses.append(Q("bool", should=citation_queries, minimum_should_match=1))

if kwargs.get("ao_requestor_type"):
# Get 'ao_requestor_type' from kwargs
ao_requestor_type = kwargs.get("ao_requestor_type", [])
ao_requestor_type_valid_values = ['1', '2', '3', '4', '5', '6', '7', '8', '9',
'10', '11', '12', '13', '14', '15', '16']

# Validate 'ao_requestor_type filter'
valid_requestor_types = filters.validate_multiselect_filter(
ao_requestor_type, ao_requestor_type_valid_values)

# Always include valid values in the query construction
if valid_requestor_types:
must_clauses.append(
Q(
"terms",
requestor_types=[
REQUESTOR_TYPES[r] for r in kwargs.get("ao_requestor_type")
REQUESTOR_TYPES[r] for r in valid_requestor_types
],
)
)
Expand Down