Skip to content

Commit 71fcdf0

Browse files
authored
Merge pull request #375 from Anko59/374-case-templates-endpoint
Case template endpoint
2 parents 82ad1ab + 55fdf07 commit 71fcdf0

File tree

8 files changed

+183
-1
lines changed

8 files changed

+183
-1
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ repos:
88
language: system
99
pass_filenames: false
1010
always_run: true
11-
stages: [push]
11+
stages: [pre-push]

tests/conftest.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from thehive4py.helpers import now_to_ts
88
from thehive4py.types.alert import InputAlert, OutputAlert
99
from thehive4py.types.case import InputCase, OutputCase
10+
from thehive4py.types.case_template import InputCaseTemplate, OutputCaseTemplate
1011
from thehive4py.types.comment import OutputComment
1112
from thehive4py.types.custom_field import OutputCustomField
1213
from thehive4py.types.observable import InputObservable, OutputObservable
@@ -113,6 +114,30 @@ def test_cases(thehive: TheHiveApi) -> List[OutputCase]:
113114
return [thehive.case.create(case=case) for case in cases]
114115

115116

117+
@pytest.fixture
118+
def test_case_template(thehive: TheHiveApi) -> OutputCaseTemplate:
119+
name = "my first case template"
120+
return thehive.case_template.create(
121+
case_template={
122+
"name": name,
123+
"description": "...",
124+
"tags": ["whatever"],
125+
}
126+
)
127+
128+
129+
@pytest.fixture
130+
def test_case_templates(thehive: TheHiveApi) -> List[OutputCaseTemplate]:
131+
case_templates: List[InputCaseTemplate] = [
132+
{"name": "my first case template", "description": "..."},
133+
{"name": "my second case template", "description": "..."},
134+
]
135+
return [
136+
thehive.case_template.create(case_template=case_template)
137+
for case_template in case_templates
138+
]
139+
140+
116141
@pytest.fixture
117142
def test_observable(thehive: TheHiveApi, test_case: OutputCase) -> OutputObservable:
118143
return thehive.observable.create_in_case(
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from typing import List
2+
3+
import pytest
4+
from thehive4py.client import TheHiveApi
5+
from thehive4py.errors import TheHiveError
6+
from thehive4py.types.case_template import InputCaseTemplate, OutputCaseTemplate
7+
8+
9+
class TestCaseTemplateEndpoint:
10+
def test_create_and_get(self, thehive: TheHiveApi):
11+
created_case_template = thehive.case_template.create(
12+
case_template={
13+
"name": "my first template",
14+
"description": "Template description",
15+
}
16+
)
17+
fetched_case_template = thehive.case_template.get(created_case_template["_id"])
18+
assert created_case_template == fetched_case_template
19+
20+
def test_update(self, thehive: TheHiveApi, test_case_template: OutputCaseTemplate):
21+
case_template_id = test_case_template["_id"]
22+
update_fields: InputCaseTemplate = {
23+
"name": "updated template name",
24+
"description": "updated template description",
25+
}
26+
thehive.case_template.update(
27+
case_template_id=case_template_id, fields=update_fields
28+
)
29+
updated_case_template = thehive.case_template.get(
30+
case_template_id=case_template_id
31+
)
32+
33+
for key, value in update_fields.items():
34+
assert updated_case_template.get(key) == value
35+
36+
def test_delete(self, thehive: TheHiveApi, test_case_template: OutputCaseTemplate):
37+
case_template_id = test_case_template["_id"]
38+
thehive.case_template.delete(case_template_id=case_template_id)
39+
with pytest.raises(TheHiveError):
40+
thehive.case_template.get(case_template_id=case_template_id)
41+
42+
def test_find(
43+
self,
44+
thehive: TheHiveApi,
45+
test_case_templates: List[OutputCaseTemplate],
46+
):
47+
found_templates = thehive.case_template.find()
48+
original_ids = [template["_id"] for template in test_case_templates]
49+
found_ids = [template["_id"] for template in found_templates]
50+
assert sorted(found_ids) == sorted(original_ids)

tests/utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,15 @@ def _reset_hive_org(hive_url: str, test_config: TestConfig, organisation: str) -
9696

9797
alerts = client.alert.find()
9898
cases = client.case.find()
99+
case_templates = client.case_template.find()
99100

100101
with ThreadPoolExecutor() as executor:
101102
executor.map(client.alert.delete, [alert["_id"] for alert in alerts])
102103
executor.map(client.case.delete, [case["_id"] for case in cases])
104+
executor.map(
105+
client.case_template.delete,
106+
[case_template["_id"] for case_template in case_templates],
107+
)
103108

104109

105110
def _reset_hive_admin_org(hive_url: str, test_config: TestConfig) -> None:

thehive4py/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from thehive4py.endpoints import (
44
AlertEndpoint,
55
CaseEndpoint,
6+
CaseTemplateEndpoint,
67
CommentEndpoint,
78
ObservableEndpoint,
89
OrganisationEndpoint,
@@ -62,6 +63,7 @@ def __init__(
6263
# case management endpoints
6364
self.alert = AlertEndpoint(self.session)
6465
self.case = CaseEndpoint(self.session)
66+
self.case_template = CaseTemplateEndpoint(self.session)
6567
self.comment = CommentEndpoint(self.session)
6668
self.observable = ObservableEndpoint(self.session)
6769
self.procedure = ProcedureEndpoint(self.session)

thehive4py/endpoints/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .alert import AlertEndpoint
22
from .case import CaseEndpoint
3+
from .case_template import CaseTemplateEndpoint
34
from .comment import CommentEndpoint
45
from .cortex import CortexEndpoint
56
from .custom_field import CustomFieldEndpoint
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from thehive4py.endpoints._base import EndpointBase
2+
from thehive4py.query import QueryExpr
3+
from thehive4py.query.filters import FilterExpr
4+
from thehive4py.query.page import Paginate
5+
from thehive4py.query.sort import SortExpr
6+
from thehive4py.types.case_template import OutputCaseTemplate, InputCaseTemplate
7+
from typing import List, Optional
8+
9+
10+
class CaseTemplateEndpoint(EndpointBase):
11+
def find(
12+
self,
13+
filters: Optional[FilterExpr] = None,
14+
sortby: Optional[SortExpr] = None,
15+
paginate: Optional[Paginate] = None,
16+
) -> List[OutputCaseTemplate]:
17+
query: QueryExpr = [
18+
{"_name": "listCaseTemplate"},
19+
*self._build_subquery(filters=filters, sortby=sortby, paginate=paginate),
20+
]
21+
22+
return self._session.make_request(
23+
"POST",
24+
path="/api/v1/query",
25+
json={"query": query},
26+
params={"name": "caseTemplate"},
27+
)
28+
29+
def get(self, case_template_id: str) -> OutputCaseTemplate:
30+
return self._session.make_request(
31+
"GET", path=f"/api/v1/caseTemplate/{case_template_id}"
32+
)
33+
34+
def create(self, case_template: InputCaseTemplate) -> OutputCaseTemplate:
35+
return self._session.make_request(
36+
"POST", path="/api/v1/caseTemplate", json=case_template
37+
)
38+
39+
def delete(self, case_template_id: str) -> None:
40+
return self._session.make_request(
41+
"DELETE", path=f"/api/v1/caseTemplate/{case_template_id}"
42+
)
43+
44+
def update(self, case_template_id: str, fields: InputCaseTemplate) -> None:
45+
return self._session.make_request(
46+
"PATCH", path=f"/api/v1/caseTemplate/{case_template_id}", json=fields
47+
)

thehive4py/types/case_template.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from typing import List, Literal, TypedDict, Union
2+
3+
from .custom_field import InputCustomFieldValue
4+
from .task import InputTask, OutputTask
5+
6+
SeverityValue = Literal[1, 2, 3, 4]
7+
TlpValue = Literal[0, 1, 2, 3, 4]
8+
PapValue = Literal[0, 1, 2, 3]
9+
10+
11+
class InputCaseTemplateRequired(TypedDict):
12+
name: str
13+
14+
15+
class InputCaseTemplate(InputCaseTemplateRequired, total=False):
16+
displayName: str
17+
titlePrefix: str
18+
description: str
19+
severity: SeverityValue
20+
tags: List[str]
21+
flag: bool
22+
tlp: TlpValue
23+
pap: PapValue
24+
summary: str
25+
tasks: List[InputTask]
26+
pageTemplateIds: List[str]
27+
customFields: Union[dict, List[InputCustomFieldValue]]
28+
29+
30+
class OutputCaseTemplateRequired(TypedDict):
31+
_id: str
32+
_type: str
33+
_createdBy: str
34+
_createdAt: int
35+
name: str
36+
37+
38+
class OutputCaseTemplate(OutputCaseTemplateRequired, total=False):
39+
_updatedBy: str
40+
_updatedAt: int
41+
displayName: str
42+
titlePrefix: str
43+
description: str
44+
severity: SeverityValue
45+
tags: List[str]
46+
flag: bool
47+
tlp: TlpValue
48+
pap: PapValue
49+
summary: str
50+
tasks: List[OutputTask]
51+
pageTemplateIds: List[str]
52+
customFields: Union[dict, List[InputCustomFieldValue]]

0 commit comments

Comments
 (0)