diff --git a/release_notes.md b/release_notes.md index 97b7f82b3..d0eb23239 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,5 +1,8 @@ # Release Notes +## 4.0.6 +- APP-4482 - [API] Updated transforms to support new static methods. + ## 4.0.5 - APP-4442 - [Mitre] Updated MITRE module to support default value and make logging optional diff --git a/tcex/__metadata__.py b/tcex/__metadata__.py index 477e3a9ff..e90c656d3 100644 --- a/tcex/__metadata__.py +++ b/tcex/__metadata__.py @@ -1,4 +1,4 @@ """TcEx Framework Module""" __license__ = 'Apache-2.0' -__version__ = '4.0.5' +__version__ = '4.0.6' diff --git a/tcex/api/tc/ti_transform/formatter.py b/tcex/api/tc/ti_transform/formatter.py deleted file mode 100644 index 39d2be101..000000000 --- a/tcex/api/tc/ti_transform/formatter.py +++ /dev/null @@ -1,8 +0,0 @@ -"""TcEx Framework Module""" - - -class TiFormatter: - """Threat Intel Common Format Method Module""" - - def __init__(self) -> None: - """Initialize instance properties.""" diff --git a/tcex/api/tc/ti_transform/model/transform_model.py b/tcex/api/tc/ti_transform/model/transform_model.py index afba4cdbb..c8c33f930 100644 --- a/tcex/api/tc/ti_transform/model/transform_model.py +++ b/tcex/api/tc/ti_transform/model/transform_model.py @@ -16,14 +16,21 @@ def _always_array(value: list | str) -> list[str]: return value +class PredefinedFunctionModel(BaseModel, extra=Extra.forbid): + """Model Definitions""" + + name: str = Field(..., description='The name of the static method.') + params: dict | None = Field({}, description='The parameters for the static method.') + + # pylint: disable=no-self-argument class TransformModel(BaseModel, extra=Extra.forbid): """Model Definition""" filter_map: dict | None = Field(None, description='') kwargs: dict | None = Field({}, description='') - method: Callable | None = Field(None, description='') - for_each: Callable | None = Field(None, description='') + method: Callable | PredefinedFunctionModel | None = Field(None, description='') + for_each: Callable | PredefinedFunctionModel | None = Field(None, description='') static_map: dict | None = Field(None, description='') @validator('filter_map', 'static_map', pre=True) diff --git a/tcex/api/tc/ti_transform/ti_predefined_functions.py b/tcex/api/tc/ti_transform/ti_predefined_functions.py new file mode 100644 index 000000000..f3d81b4bb --- /dev/null +++ b/tcex/api/tc/ti_transform/ti_predefined_functions.py @@ -0,0 +1,21 @@ +"""TcEx Framework Module""" + +# first-party +from tcex.util import Util + +to_upper_case = str.upper +to_lower_case = str.lower +to_title_case = str.title + +prepend = '{prefix} {}'.format +append = '{} {suffix}'.format + + +def replace(value: str, find: str, replace_with: str) -> str: + """Replace value in string.""" + return value.replace(find, replace_with) + + +def format_datetime(value: str): + """Format datetime.""" + return Util.any_to_datetime(value).strftime('%Y-%m-%dT%H:%M:%SZ') diff --git a/tcex/api/tc/ti_transform/transform_abc.py b/tcex/api/tc/ti_transform/transform_abc.py index 6ae73c03d..6a4f1ea55 100644 --- a/tcex/api/tc/ti_transform/transform_abc.py +++ b/tcex/api/tc/ti_transform/transform_abc.py @@ -14,6 +14,7 @@ from jmespath import functions # first-party +from tcex.api.tc.ti_transform import ti_predefined_functions from tcex.api.tc.ti_transform.model import AttributeTransformModel # TYPE-CHECKING from tcex.api.tc.ti_transform.model import SecurityLabelTransformModel # TYPE-CHECKING from tcex.api.tc.ti_transform.model import TagTransformModel # TYPE-CHECKING @@ -26,6 +27,7 @@ AssociatedGroupTransform, DatetimeTransformModel, FileOccurrenceTransformModel, + PredefinedFunctionModel, ) from tcex.logger.trace_logger import TraceLogger from tcex.util import Util @@ -491,10 +493,16 @@ def _transform_value(self, metadata: MetadataTransformModel | None) -> str | Non return value def _transform_value_callable( - self, value: dict | list | str, c: Callable, kwargs=None + self, value: dict | list | str, c: Callable | PredefinedFunctionModel, kwargs=None ) -> str | None | list[str] | None: """Transform values in the TI data.""" # find signature of method and call with correct args + if isinstance(c, PredefinedFunctionModel): + normalized_params = { + k.replace(' ', '_').lower(): v for k, v in PredefinedFunctionModel.params or {} + } + return getattr(ti_predefined_functions, c.name)(value, **normalized_params) + kwargs = kwargs or {} try: sig = signature(c, follow_wrapped=True)