Skip to content

Commit 50997e9

Browse files
committed
Feature: create & search tags
1 parent d29cd18 commit 50997e9

File tree

3 files changed

+86
-5
lines changed

3 files changed

+86
-5
lines changed

tests/api.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,38 @@ def test_tag_object(self, mock_post):
278278
json={"tags": ["tag1"], "ids": ["1"]},
279279
)
280280

281+
@patch("yeti.api.requests.Session.post")
282+
def test_new_tag(self, mock_post):
283+
mock_response = MagicMock()
284+
mock_response.content = b'{"name": "testtag"}'
285+
mock_post.return_value = mock_response
286+
287+
result = self.api.new_tag("testtag")
288+
self.assertEqual(result, {"name": "testtag"})
289+
mock_post.assert_called_with(
290+
"http://fake-url/api/v2/tags/",
291+
json={"name": "testtag"},
292+
)
293+
294+
result = self.api.new_tag("wdesc", description="desc")
295+
mock_post.assert_called_with(
296+
"http://fake-url/api/v2/tags/",
297+
json={"name": "wdesc", "description": "desc"},
298+
)
299+
300+
@patch("yeti.api.requests.Session.post")
301+
def test_search_tags(self, mock_post):
302+
mock_response = MagicMock()
303+
mock_response.content = b'{"tags": [{"name": "tag1"}]}'
304+
mock_post.return_value = mock_response
305+
306+
result = self.api.search_tags("tag1")
307+
self.assertEqual(result, [{"name": "tag1"}])
308+
mock_post.assert_called_with(
309+
"http://fake-url/api/v2/tags/search",
310+
json={"name": "tag1", "count": 100, "page": 0},
311+
)
312+
281313
@patch("yeti.api.requests.Session.post")
282314
def test_link_objects(self, mock_post):
283315
mock_response = MagicMock()

tests/e2e.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import os
22
import time
33
import unittest
4-
from unittest.mock import MagicMock, patch
5-
6-
import requests
74

85
from yeti import errors
96
from yeti.api import YetiApi
@@ -85,7 +82,7 @@ def test_find_indicator(self):
8582
self.assertEqual(indicator["name"], "testGet")
8683
self.assertEqual(indicator["pattern"], "test[0-9]")
8784
self.assertEqual(indicator["tags"][0]["name"], "testtag")
88-
85+
8986
def test_link_objects(self):
9087
self.api.auth_api_key(os.getenv("YETI_API_KEY"))
9188
indicator = self.api.new_indicator(
@@ -120,4 +117,19 @@ def test_link_objects(self):
120117
self.assertEqual(len(neighbors["vertices"]), 1)
121118
self.assertEqual(
122119
neighbors["vertices"][f'entities/{malware["id"]}']["name"], "testMalware"
123-
)
120+
)
121+
122+
def test_new_tag(self):
123+
self.api.auth_api_key(os.getenv("YETI_API_KEY"))
124+
tag = self.api.new_tag({"name": "testTag", "description": "test"})
125+
self.assertEqual(tag["name"], "testTag")
126+
self.assertEqual(tag["description"], "test")
127+
128+
def test_search_tags(self):
129+
self.api.auth_api_key(os.getenv("YETI_API_KEY"))
130+
self.api.new_tag({"name": "testSearchTag", "description": "test"})
131+
time.sleep(5)
132+
tags = self.api.search_tags(name="test")
133+
self.assertEqual(len(tags), 1)
134+
self.assertEqual(tags[0]["name"], "testSearchTag")
135+
self.assertEqual(tags[0]["description"], "test")

yeti/api.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,43 @@ def tag_object(
739739
)
740740
return json.loads(response)
741741

742+
def new_tag(self, name: str, description: str | None = None) -> dict[str, Any]:
743+
"""Creates a new tag in Yeti.
744+
745+
Args:
746+
name: The name of the tag to create.
747+
description: An optional description for the tag.
748+
749+
Returns:
750+
The response from the API; a dict representing the tag.
751+
"""
752+
params = {"name": name}
753+
if description:
754+
params["description"] = description
755+
response = self.do_request(
756+
"POST", f"{self._url_root}/api/v2/tags/", json_data=params
757+
)
758+
return json.loads(response)
759+
760+
def search_tags(self, name: str, count: int = 100, page: int = 0):
761+
"""Searches for tags in Yeti.
762+
763+
Returns tag information based on a substring match of the tag name.
764+
765+
Args:
766+
name: The name of the tag to search for (substring match).
767+
count: The number of results to return (default is 100).
768+
page: The page of results to return (default is 0, which means the first page).
769+
770+
Returns:
771+
The response from the API; a list of dicts representing tags.
772+
"""
773+
params = {"name": name, "count": count, "page": page}
774+
response = self.do_request(
775+
"POST", f"{self._url_root}/api/v2/tags/search", json_data=params
776+
)
777+
return json.loads(response)["tags"]
778+
742779
def link_objects(
743780
self,
744781
source: YetiObject,

0 commit comments

Comments
 (0)