diff --git a/pyproject.toml b/pyproject.toml index 4056d31..8050ea8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "yeti-python" -version = "2.3.0" +version = "2.3.1" description = "Python bindings for the Yeti API" authors = ["tomchop"] license = "Apache" diff --git a/tests/api.py b/tests/api.py index 4a90d9f..6374734 100644 --- a/tests/api.py +++ b/tests/api.py @@ -239,12 +239,12 @@ def test_new_dfiq_from_yaml(self, mock_post): mock_response.content = b'{"id": "new_dfiq"}' mock_post.return_value = mock_response - result = self.api.new_dfiq_from_yaml("type", "yaml_content") + with self.assertWarns(DeprecationWarning): + result = self.api.new_dfiq_from_yaml("type", "yaml_content") self.assertEqual(result, {"id": "new_dfiq"}) mock_post.assert_called_with( "http://fake-url/api/v2/dfiq/from_yaml", json={ - "dfiq_type": "type", "dfiq_yaml": "yaml_content", }, ) @@ -255,12 +255,12 @@ def test_patch_dfiq_from_yaml(self, mock_patch): mock_response.content = b'{"id": "patched_dfiq"}' mock_patch.return_value = mock_response - result = self.api.patch_dfiq_from_yaml("type", "yaml_content", 1) + with self.assertWarns(DeprecationWarning): + result = self.api.patch_dfiq_from_yaml("type", "yaml_content", 1) self.assertEqual(result, {"id": "patched_dfiq"}) mock_patch.assert_called_with( "http://fake-url/api/v2/dfiq/1", json={ - "dfiq_type": "type", "dfiq_yaml": "yaml_content", }, ) @@ -279,7 +279,6 @@ def test_patch_dfiq(self, mock_patch): "http://fake-url/api/v2/dfiq/1", json={ "dfiq_object": {"name": "patched_dfiq", "type": "question", "id": 1}, - "dfiq_type": "question", }, ) diff --git a/tests/e2e.py b/tests/e2e.py index 45c66e3..4409fde 100644 --- a/tests/e2e.py +++ b/tests/e2e.py @@ -1,6 +1,7 @@ import os import time import unittest +import uuid from yeti import errors from yeti.api import YetiApi @@ -204,6 +205,38 @@ def test_link_objects(self): neighbors["vertices"][f'entities/{malware["id"]}']["name"], "testMalware" ) + def test_dfiq_from_yaml_and_patch(self): + """Covers the three DFIQ write methods, whose payloads the API + validates with extra="forbid": an unexpected field is a 422.""" + self.api.auth_api_key(os.getenv("YETI_API_KEY")) + scenario_uuid = str(uuid.uuid4()) + scenario_yaml = f"""--- +name: testScenario +type: scenario +description: > + test +id: S1990 +uuid: {scenario_uuid} +dfiq_version: 1.1.0 +""" + + with self.assertWarns(DeprecationWarning): + scenario = self.api.new_dfiq_from_yaml("scenario", scenario_yaml) + self.assertEqual(scenario["name"], "testScenario") + self.assertEqual(scenario["uuid"], scenario_uuid) + + with self.assertWarns(DeprecationWarning): + patched = self.api.patch_dfiq_from_yaml( + "scenario", + scenario_yaml.replace(" test", " patched from yaml"), + scenario["id"], + ) + self.assertEqual(patched["description"].strip(), "patched from yaml") + + patched["description"] = "patched as an object" + patched = self.api.patch_dfiq(patched) + self.assertEqual(patched["description"], "patched as an object") + def test_new_tag(self): self.api.auth_api_key(os.getenv("YETI_API_KEY")) tag = self.api.new_tag("testTag", description="test") diff --git a/yeti/api.py b/yeti/api.py index f6d9596..59f8aad 100644 --- a/yeti/api.py +++ b/yeti/api.py @@ -3,6 +3,7 @@ import json import logging import urllib.parse +import warnings from typing import Any, Sequence import requests @@ -20,6 +21,11 @@ OIDC_CALLBACK_ENDPOINT = "/api/v2/auth/oidc-callback-token" API_TOKEN_ENDPOINT = "/api/v2/auth/api-token" +DFIQ_TYPE_DEPRECATION = ( + "dfiq_type is ignored: Yeti infers the DFIQ type from the payload. The " + "parameter will be removed in the next major version." +) + SUPPORTED_IOC_TYPES = [ "generic", @@ -702,9 +708,14 @@ def get_multiple_dfiq( return json.loads(response)["dfiq"] def new_dfiq_from_yaml(self, dfiq_type: str, dfiq_yaml: str) -> YetiObject: - """Creates a new DFIQ object in Yeti from a YAML string.""" + """Creates a new DFIQ object in Yeti from a YAML string. + + Args: + dfiq_type: Ignored. Yeti reads the type from the YAML. + dfiq_yaml: The DFIQ object, as YAML. + """ + warnings.warn(DFIQ_TYPE_DEPRECATION, DeprecationWarning, stacklevel=2) params = { - "dfiq_type": dfiq_type, "dfiq_yaml": dfiq_yaml, } response = self.do_request( @@ -718,9 +729,15 @@ def patch_dfiq_from_yaml( dfiq_yaml: str, yeti_id: int, ) -> YetiObject: - """Patches a DFIQ object in Yeti from a YAML string.""" + """Patches a DFIQ object in Yeti from a YAML string. + + Args: + dfiq_type: Ignored. Yeti reads the type from the YAML. + dfiq_yaml: The DFIQ object, as YAML. + yeti_id: The ID of the DFIQ object to patch. + """ + warnings.warn(DFIQ_TYPE_DEPRECATION, DeprecationWarning, stacklevel=2) params = { - "dfiq_type": dfiq_type, "dfiq_yaml": dfiq_yaml, } response = self.do_request( @@ -731,7 +748,6 @@ def patch_dfiq_from_yaml( def patch_dfiq(self, dfiq_object: dict[str, Any]) -> YetiObject: """Patches a DFIQ object in Yeti.""" params = { - "dfiq_type": dfiq_object["type"], "dfiq_object": dfiq_object, } response = self.do_request(