From 094581e270ac60e206510ef7684cf8120537f046 Mon Sep 17 00:00:00 2001 From: Bracey Summers Date: Tue, 27 Feb 2024 13:51:59 -0600 Subject: [PATCH] APP-4383 - [API] Updated TC API module for changes in the V3 API APP-4401 - [API] Fixed issue with Assignee model not calculating appropriate model type --- tcex/api/tc/v3/_gen/_gen_filter_abc.py | 21 ++++++++++++++++++ tcex/api/tc/v3/cases/case_filter.py | 22 +++++++------------ tcex/api/tc/v3/groups/group_filter.py | 22 +++++++------------ tcex/api/tc/v3/indicators/indicator_filter.py | 22 +++++++------------ .../intel_requirement_filter.py | 22 +++++++------------ tcex/api/tc/v3/v3_model_abc.py | 2 +- tcex/api/tc/v3/victims/victim_filter.py | 22 +++++++------------ tests/api/tc/v3/cases/test_case_interface.py | 1 + tests/api/tc/v3/v3_helpers.py | 9 +++++--- 9 files changed, 69 insertions(+), 74 deletions(-) diff --git a/tcex/api/tc/v3/_gen/_gen_filter_abc.py b/tcex/api/tc/v3/_gen/_gen_filter_abc.py index a99d9c6fb..f46286bf9 100644 --- a/tcex/api/tc/v3/_gen/_gen_filter_abc.py +++ b/tcex/api/tc/v3/_gen/_gen_filter_abc.py @@ -315,6 +315,25 @@ def _gen_code_has_tag_method(self) -> list: '', ] + def _gen_code_has_all_tags_method(self) -> list: + """Return code for has_tag TQL filter methods.""" + self._add_tql_imports() + return [ + f'{self.i1}@property', + f'{self.i1}def has_all_tags(self):', + f'{self.i2}"""Return **TagFilter** for further filtering."""', + f'{self.i2}# first-party', + f'{self.i2}from tcex.api.tc.v3.tags.tag_filter import TagFilter', + '', + f'{self.i2}tags = TagFilter(Tql())', + ( + f'''{self.i2}self._tql.add_filter('hasAllTags', ''' + '''TqlOperator.EQ, tags, TqlType.SUB_QUERY)''' + ), + f'{self.i2}return tags', + '', + ] + def _gen_code_has_task_method(self) -> list: """Return code for has_task TQL filter methods.""" self._add_tql_imports() @@ -495,6 +514,8 @@ def gen_class_methods(self): _filter_class.extend(self._gen_code_has_note_method()) elif f.keyword.snake_case() == 'has_tag': _filter_class.extend(self._gen_code_has_tag_method()) + elif f.keyword.snake_case() == 'has_all_tags': + _filter_class.extend(self._gen_code_has_all_tags_method()) elif f.keyword.snake_case() == 'has_task': _filter_class.extend(self._gen_code_has_task_method()) elif f.keyword.snake_case() == 'has_attribute': diff --git a/tcex/api/tc/v3/cases/case_filter.py b/tcex/api/tc/v3/cases/case_filter.py index 30ed30293..d51126b50 100644 --- a/tcex/api/tc/v3/cases/case_filter.py +++ b/tcex/api/tc/v3/cases/case_filter.py @@ -267,21 +267,15 @@ def description(self, operator: Enum, description: list | str): self._tql.add_filter('description', operator, description, TqlType.STRING) - def has_all_tags(self, operator: Enum, has_all_tags: int | list): - """Filter All Tags based on **hasAllTags** keyword. - - Args: - operator: The operator enum for the filter. - has_all_tags: Filters data tagged with all provided Tag IDs. Query must only contain - 'id=x' or 'id IN (x,y,z)'. - """ - if isinstance(has_all_tags, list) and operator not in self.list_types: - raise RuntimeError( - 'Operator must be CONTAINS, NOT_CONTAINS, IN' - 'or NOT_IN when filtering on a list of values.' - ) + @property + def has_all_tags(self): + """Return **TagFilter** for further filtering.""" + # first-party + from tcex.api.tc.v3.tags.tag_filter import TagFilter - self._tql.add_filter('hasAllTags', operator, has_all_tags, TqlType.INTEGER) + tags = TagFilter(Tql()) + self._tql.add_filter('hasAllTags', TqlOperator.EQ, tags, TqlType.SUB_QUERY) + return tags @property def has_artifact(self): diff --git a/tcex/api/tc/v3/groups/group_filter.py b/tcex/api/tc/v3/groups/group_filter.py index e4f602e39..6c9a32120 100644 --- a/tcex/api/tc/v3/groups/group_filter.py +++ b/tcex/api/tc/v3/groups/group_filter.py @@ -321,21 +321,15 @@ def generated_report(self, operator: Enum, generated_report: bool): """ self._tql.add_filter('generatedReport', operator, generated_report, TqlType.BOOLEAN) - def has_all_tags(self, operator: Enum, has_all_tags: int | list): - """Filter All Tags based on **hasAllTags** keyword. - - Args: - operator: The operator enum for the filter. - has_all_tags: Filters data tagged with all provided Tag IDs. Query must only contain - 'id=x' or 'id IN (x,y,z)'. - """ - if isinstance(has_all_tags, list) and operator not in self.list_types: - raise RuntimeError( - 'Operator must be CONTAINS, NOT_CONTAINS, IN' - 'or NOT_IN when filtering on a list of values.' - ) + @property + def has_all_tags(self): + """Return **TagFilter** for further filtering.""" + # first-party + from tcex.api.tc.v3.tags.tag_filter import TagFilter - self._tql.add_filter('hasAllTags', operator, has_all_tags, TqlType.INTEGER) + tags = TagFilter(Tql()) + self._tql.add_filter('hasAllTags', TqlOperator.EQ, tags, TqlType.SUB_QUERY) + return tags @property def has_artifact(self): diff --git a/tcex/api/tc/v3/indicators/indicator_filter.py b/tcex/api/tc/v3/indicators/indicator_filter.py index 7b2bb4214..e47b72fc0 100644 --- a/tcex/api/tc/v3/indicators/indicator_filter.py +++ b/tcex/api/tc/v3/indicators/indicator_filter.py @@ -411,21 +411,15 @@ def first_seen(self, operator: Enum, first_seen: Arrow | datetime | int | str): first_seen = self.util.any_to_datetime(first_seen).strftime('%Y-%m-%d %H:%M:%S') self._tql.add_filter('firstSeen', operator, first_seen, TqlType.STRING) - def has_all_tags(self, operator: Enum, has_all_tags: int | list): - """Filter All Tags based on **hasAllTags** keyword. - - Args: - operator: The operator enum for the filter. - has_all_tags: Filters data tagged with all provided Tag IDs. Query must only contain - 'id=x' or 'id IN (x,y,z)'. - """ - if isinstance(has_all_tags, list) and operator not in self.list_types: - raise RuntimeError( - 'Operator must be CONTAINS, NOT_CONTAINS, IN' - 'or NOT_IN when filtering on a list of values.' - ) + @property + def has_all_tags(self): + """Return **TagFilter** for further filtering.""" + # first-party + from tcex.api.tc.v3.tags.tag_filter import TagFilter - self._tql.add_filter('hasAllTags', operator, has_all_tags, TqlType.INTEGER) + tags = TagFilter(Tql()) + self._tql.add_filter('hasAllTags', TqlOperator.EQ, tags, TqlType.SUB_QUERY) + return tags @property def has_artifact(self): diff --git a/tcex/api/tc/v3/intel_requirements/intel_requirement_filter.py b/tcex/api/tc/v3/intel_requirements/intel_requirement_filter.py index 224604c24..928b6ada2 100644 --- a/tcex/api/tc/v3/intel_requirements/intel_requirement_filter.py +++ b/tcex/api/tc/v3/intel_requirements/intel_requirement_filter.py @@ -321,21 +321,15 @@ def generated_report(self, operator: Enum, generated_report: bool): """ self._tql.add_filter('generatedReport', operator, generated_report, TqlType.BOOLEAN) - def has_all_tags(self, operator: Enum, has_all_tags: int | list): - """Filter All Tags based on **hasAllTags** keyword. - - Args: - operator: The operator enum for the filter. - has_all_tags: Filters data tagged with all provided Tag IDs. Query must only contain - 'id=x' or 'id IN (x,y,z)'. - """ - if isinstance(has_all_tags, list) and operator not in self.list_types: - raise RuntimeError( - 'Operator must be CONTAINS, NOT_CONTAINS, IN' - 'or NOT_IN when filtering on a list of values.' - ) + @property + def has_all_tags(self): + """Return **TagFilter** for further filtering.""" + # first-party + from tcex.api.tc.v3.tags.tag_filter import TagFilter - self._tql.add_filter('hasAllTags', operator, has_all_tags, TqlType.INTEGER) + tags = TagFilter(Tql()) + self._tql.add_filter('hasAllTags', TqlOperator.EQ, tags, TqlType.SUB_QUERY) + return tags @property def has_artifact(self): diff --git a/tcex/api/tc/v3/v3_model_abc.py b/tcex/api/tc/v3/v3_model_abc.py index 78eab0a62..db02ef7d7 100644 --- a/tcex/api/tc/v3/v3_model_abc.py +++ b/tcex/api/tc/v3/v3_model_abc.py @@ -34,7 +34,7 @@ def default(self, o: Any) -> str: return o -class V3ModelABC(BaseModel, ABC): +class V3ModelABC(BaseModel, ABC, allow_population_by_field_name=True): """V3 Base Model""" _associated_type = PrivateAttr(False) diff --git a/tcex/api/tc/v3/victims/victim_filter.py b/tcex/api/tc/v3/victims/victim_filter.py index 276e1cdb3..88786eafe 100644 --- a/tcex/api/tc/v3/victims/victim_filter.py +++ b/tcex/api/tc/v3/victims/victim_filter.py @@ -93,21 +93,15 @@ def description(self, operator: Enum, description: list | str): self._tql.add_filter('description', operator, description, TqlType.STRING) - def has_all_tags(self, operator: Enum, has_all_tags: int | list): - """Filter All Tags based on **hasAllTags** keyword. - - Args: - operator: The operator enum for the filter. - has_all_tags: Filters data tagged with all provided Tag IDs. Query must only contain - 'id=x' or 'id IN (x,y,z)'. - """ - if isinstance(has_all_tags, list) and operator not in self.list_types: - raise RuntimeError( - 'Operator must be CONTAINS, NOT_CONTAINS, IN' - 'or NOT_IN when filtering on a list of values.' - ) + @property + def has_all_tags(self): + """Return **TagFilter** for further filtering.""" + # first-party + from tcex.api.tc.v3.tags.tag_filter import TagFilter - self._tql.add_filter('hasAllTags', operator, has_all_tags, TqlType.INTEGER) + tags = TagFilter(Tql()) + self._tql.add_filter('hasAllTags', TqlOperator.EQ, tags, TqlType.SUB_QUERY) + return tags @property def has_attribute(self): diff --git a/tests/api/tc/v3/cases/test_case_interface.py b/tests/api/tc/v3/cases/test_case_interface.py index aca70dbf4..39dbef4ee 100644 --- a/tests/api/tc/v3/cases/test_case_interface.py +++ b/tests/api/tc/v3/cases/test_case_interface.py @@ -376,6 +376,7 @@ def test_case_all_filters(self, request: FixtureRequest): case_occurrence_time = datetime.now() - timedelta(days=20) assignee = {'type': 'User', 'data': {'user_name': os.getenv('TC_API_ACCESS_ID')}} + # assignee = {'type': 'Group', 'data': {'name': 'TcEx Testing'}} # [Create Testing] define case data case_data = { diff --git a/tests/api/tc/v3/v3_helpers.py b/tests/api/tc/v3/v3_helpers.py index aa11c6438..9f6037af6 100644 --- a/tests/api/tc/v3/v3_helpers.py +++ b/tests/api/tc/v3/v3_helpers.py @@ -757,12 +757,12 @@ def obj_api_options(self): ] if self.v3_helper.v3_object == 'indicators': + if 'associationName' in names: + # fix discrepancy between /fields and + names = ['customAssociationNames'] if 'genericCustomIndicatorValues' in names: # fix discrepancy between /fields and names = ['value1', 'value2', 'value3'] - if 'whoIs' in names: - # fix discrepancy between /fields and - names = ['whois'] if 'threatAssess' in names: # fix discrepancy between /fields and names = [ @@ -772,6 +772,9 @@ def obj_api_options(self): 'threatAssessScoreFalsePositive', 'threatAssessScoreObserved', ] + if 'whoIs' in names: + # fix discrepancy between /fields and + names = ['whois'] if self.v3_helper.v3_object == 'results': if 'intelRequirementDetails' in names: