Skip to content

Commit 88702dd

Browse files
authored
Merge pull request #17 from yeti-platform/getmultiple
Add get_multiple* endpoints
2 parents dd18c24 + 5856484 commit 88702dd

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed

tests/api.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ def test_search_indicators(self, mock_post):
4646
},
4747
)
4848

49+
@patch("yeti.api.requests.Session.post")
50+
def test_get_multiple_indicators(self, mock_post):
51+
mock_response = MagicMock()
52+
mock_response.content = b'{"indicators": [{"name": "test"}]}'
53+
mock_post.return_value = mock_response
54+
55+
result = self.api.get_multiple_indicators(["test"])
56+
self.assertEqual(result, [{"name": "test"}])
57+
mock_post.assert_called_with(
58+
"http://fake-url/api/v2/indicators/get/multiple",
59+
json={"names": ["test"], "count": 100, "page": 0},
60+
)
61+
4962
@patch("yeti.api.requests.Session.post")
5063
def test_search_entities(self, mock_post):
5164
mock_response = MagicMock()
@@ -65,6 +78,19 @@ def test_search_entities(self, mock_post):
6578
},
6679
)
6780

81+
@patch("yeti.api.requests.Session.post")
82+
def test_get_multiple_entities(self, mock_post):
83+
mock_response = MagicMock()
84+
mock_response.content = b'{"entities": [{"name": "test_entity"}]}'
85+
mock_post.return_value = mock_response
86+
87+
result = self.api.get_multiple_entities(["test_entity"])
88+
self.assertEqual(result, [{"name": "test_entity"}])
89+
mock_post.assert_called_with(
90+
"http://fake-url/api/v2/entities/get/multiple",
91+
json={"names": ["test_entity"], "count": 100, "page": 0},
92+
)
93+
6894
@patch("yeti.api.requests.Session.post")
6995
def test_search_observables(self, mock_post):
7096
mock_response = MagicMock()
@@ -159,6 +185,19 @@ def test_search_dfiq(self, mock_post):
159185
},
160186
)
161187

188+
@patch("yeti.api.requests.Session.post")
189+
def test_get_multiple_dfiq(self, mock_post):
190+
mock_response = MagicMock()
191+
mock_response.content = b'{"dfiq": [{"name": "test_dfiq"}]}'
192+
mock_post.return_value = mock_response
193+
194+
result = self.api.get_multiple_dfiq(["test_dfiq"])
195+
self.assertEqual(result, [{"name": "test_dfiq"}])
196+
mock_post.assert_called_with(
197+
"http://fake-url/api/v2/dfiq/get/multiple",
198+
json={"names": ["test_dfiq"], "count": 100, "page": 0},
199+
)
200+
162201
@patch("yeti.api.requests.Session.post")
163202
def test_new_dfiq_from_yaml(self, mock_post):
164203
mock_response = MagicMock()

tests/e2e.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,46 @@ def test_auth_refresh(self):
4444

4545
self.api.search_indicators(name="test")
4646

47+
def test_search_entities(self):
48+
self.api.auth_api_key(os.getenv("YETI_API_KEY"))
49+
self.api.new_entity(
50+
{
51+
"name": "testSearch",
52+
"type": "malware",
53+
"description": "test",
54+
},
55+
tags=["testtag"],
56+
)
57+
time.sleep(5)
58+
result = self.api.search_entities(name="testSear", description="tes")
59+
self.assertEqual(len(result), 1, result)
60+
self.assertEqual(result[0]["name"], "testSearch")
61+
self.assertEqual(result[0]["tags"][0]["name"], "testtag")
62+
63+
def test_get_multiple_entities(self):
64+
self.api.auth_api_key(os.getenv("YETI_API_KEY"))
65+
self.api.new_entity(
66+
{
67+
"name": "testGet1",
68+
"type": "malware",
69+
"description": "test",
70+
},
71+
tags=["testtag1"],
72+
)
73+
self.api.new_entity(
74+
{
75+
"name": "testGet2",
76+
"type": "malware",
77+
"description": "test",
78+
},
79+
tags=["testtag2"],
80+
)
81+
time.sleep(5)
82+
entities = self.api.get_multiple_entities(["testGet1", "testGet2"])
83+
self.assertEqual(len(entities), 2)
84+
names = [entity["name"] for entity in entities]
85+
self.assertCountEqual(names, ["testGet1", "testGet2"])
86+
4787
def test_search_indicators(self):
4888
self.api.auth_api_key(os.getenv("YETI_API_KEY"))
4989
self.api.new_indicator(
@@ -83,6 +123,34 @@ def test_find_indicator(self):
83123
self.assertEqual(indicator["pattern"], "test[0-9]")
84124
self.assertEqual(indicator["tags"][0]["name"], "testtag")
85125

126+
def test_get_multiple_indicators(self):
127+
self.api.auth_api_key(os.getenv("YETI_API_KEY"))
128+
self.api.new_indicator(
129+
{
130+
"name": "testGet1",
131+
"type": "regex",
132+
"description": "test",
133+
"pattern": "test[0-9]",
134+
"diamond": "victim",
135+
},
136+
tags=["testtag1"],
137+
)
138+
self.api.new_indicator(
139+
{
140+
"name": "testGet2",
141+
"type": "regex",
142+
"description": "test",
143+
"pattern": "test[0-9]",
144+
"diamond": "victim",
145+
},
146+
tags=["testtag2"],
147+
)
148+
time.sleep(5)
149+
indicators = self.api.get_multiple_indicators(["testGet1", "testGet2"])
150+
self.assertEqual(len(indicators), 2)
151+
names = [indicator["name"] for indicator in indicators]
152+
self.assertCountEqual(names, ["testGet1", "testGet2"])
153+
86154
def test_link_objects(self):
87155
self.api.auth_api_key(os.getenv("YETI_API_KEY"))
88156
indicator = self.api.new_indicator(

yeti/api.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,25 @@ def search_indicators(
253253
)
254254
return json.loads(response)["indicators"]
255255

256+
def get_multiple_indicators(
257+
self, names: list[str], count: int = 100, page: int = 0
258+
) -> list[YetiObject]:
259+
"""Gets a list of indicators by name.
260+
261+
Args:
262+
names: The list of indicator names to retrieve.
263+
count: The number of results to return (default is 100).
264+
page: The page of results to return (default is 0, which means the first page).
265+
266+
Returns:
267+
A list of dicts representing the indicators.
268+
"""
269+
params = {"names": names, "count": count, "page": page}
270+
response = self.do_request(
271+
"POST", f"{self._url_root}/api/v2/indicators/get/multiple", json_data=params
272+
)
273+
return json.loads(response)["indicators"]
274+
256275
def find_entity(self, name: str, type: str) -> YetiObject | None:
257276
"""Finds an entity in Yeti by name.
258277
@@ -316,6 +335,25 @@ def search_entities(
316335
)
317336
return json.loads(response)["entities"]
318337

338+
def get_multiple_entities(
339+
self, names: list[str], count: int = 100, page: int = 0
340+
) -> list[YetiObject]:
341+
"""Gets a list of entities by name.
342+
343+
Args:
344+
names: The list of entity names to retrieve.
345+
count: The number of results to return (default is 100).
346+
page: The page of results to return (default is 0, which means the first page).
347+
348+
Returns:
349+
A list of dicts representing the entities.
350+
"""
351+
params = {"names": names, "count": count, "page": page}
352+
response = self.do_request(
353+
"POST", f"{self._url_root}/api/v2/entities/get/multiple", json_data=params
354+
)
355+
return json.loads(response)["entities"]
356+
319357
def find_observable(self, value: str, type: str) -> YetiObject | None:
320358
"""Finds an observable in Yeti by value and type.
321359
@@ -594,6 +632,25 @@ def search_dfiq(
594632
)
595633
return json.loads(response)["dfiq"]
596634

635+
def get_multiple_dfiq(
636+
self, names: list[str], count: int = 100, page: int = 0
637+
) -> list[YetiObject]:
638+
"""Gets a list of DFIQ objects by name.
639+
640+
Args:
641+
names: The list of DFIQ names to retrieve.
642+
count: The number of results to return (default is 100).
643+
page: The page of results to return (default is 0, which means the first page).
644+
645+
Returns:
646+
A list of dicts representing the DFIQ objects.
647+
"""
648+
params = {"names": names, "count": count, "page": page}
649+
response = self.do_request(
650+
"POST", f"{self._url_root}/api/v2/dfiq/get/multiple", json_data=params
651+
)
652+
return json.loads(response)["dfiq"]
653+
597654
def new_dfiq_from_yaml(self, dfiq_type: str, dfiq_yaml: str) -> YetiObject:
598655
"""Creates a new DFIQ object in Yeti from a YAML string."""
599656
params = {
@@ -776,6 +833,25 @@ def search_tags(self, name: str, count: int = 100, page: int = 0):
776833
)
777834
return json.loads(response)["tags"]
778835

836+
def get_multiple_tags(
837+
self, names: list[str], count: int = 100, page: int = 0
838+
) -> list[dict[str, Any]]:
839+
"""Gets a list of tags by name.
840+
841+
Args:
842+
names: The list of tag names to retrieve.
843+
count: The number of results to return (default is 100).
844+
page: The page of results to return (default is 0, which means the first page).
845+
846+
Returns:
847+
A list of dicts representing the tags.
848+
"""
849+
params = {"names": names, "count": count, "page": page}
850+
response = self.do_request(
851+
"POST", f"{self._url_root}/api/v2/tags/get/multiple", json_data=params
852+
)
853+
return json.loads(response)["tags"]
854+
779855
def link_objects(
780856
self,
781857
source: YetiObject,

0 commit comments

Comments
 (0)