Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.

Commit 6bc9897

Browse files
authored
[doc] Add docstrings to api (#86)
1 parent 77d339a commit 6bc9897

File tree

6 files changed

+302
-31
lines changed

6 files changed

+302
-31
lines changed

pycti/api/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# -*- coding: utf-8 -*-
2+
"""init for pycti.api
3+
"""
24

35
from .opencti_api_client import OpenCTIApiClient
46
from .opencti_api_connector import OpenCTIApiConnector

pycti/api/opencti_api_client.py

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,22 @@ def __init__(self, name, data, mime="text/plain"):
4848

4949

5050
class OpenCTIApiClient:
51-
"""
52-
Python API for OpenCTI
53-
:param url: OpenCTI URL
54-
:param token: The API key
51+
"""Main API client for OpenCTI
52+
53+
:param url: OpenCTI API url
54+
:type url: str
55+
:param token: OpenCTI API token
56+
:type token: str
57+
:param log_level: log level for the client
58+
:type log_level: str, optional
59+
:param ssl_verify:
60+
:type ssl_verify: bool, optional
5561
"""
5662

5763
def __init__(self, url, token, log_level="info", ssl_verify=False):
64+
"""Constructor method
65+
"""
66+
5867
# Check configuration
5968
self.ssl_verify = ssl_verify
6069
if url is None or len(token) == 0:
@@ -114,12 +123,34 @@ def __init__(self, url, token, log_level="info", ssl_verify=False):
114123
)
115124

116125
def get_token(self):
126+
"""Get the API token
127+
128+
:return: returns the configured API token
129+
:rtype: str
130+
"""
131+
117132
return self.api_token
118133

119134
def set_token(self, token):
135+
"""set the request header with the specified token
136+
137+
:param token: OpenCTI API token
138+
:type token: str
139+
"""
140+
120141
self.request_headers = {"Authorization": "Bearer " + token}
121142

122143
def query(self, query, variables={}):
144+
"""submit a query to the OpenCTI GraphQL API
145+
146+
:param query: GraphQL query string
147+
:type query: str
148+
:param variables: GraphQL query variables, defaults to {}
149+
:type variables: dict, optional
150+
:return: returns the response json content
151+
:rtype: Any
152+
"""
153+
123154
query_var = {}
124155
files_vars = []
125156
# Implementation of spec https://github.com/jaydenseric/graphql-multipart-request-spec
@@ -210,7 +241,7 @@ def query(self, query, variables={}):
210241
verify=self.ssl_verify,
211242
)
212243
# Build response
213-
if r.status_code == requests.codes.ok:
244+
if r.status_code == 200:
214245
result = r.json()
215246
if "errors" in result:
216247
logging.error(result["errors"][0]["message"])
@@ -220,12 +251,30 @@ def query(self, query, variables={}):
220251
logging.info(r.text)
221252

222253
def fetch_opencti_file(self, fetch_uri, binary=False):
254+
"""get file from the OpenCTI API
255+
256+
:param fetch_uri: download URI to use
257+
:type fetch_uri: str
258+
:param binary: [description], defaults to False
259+
:type binary: bool, optional
260+
:return: returns either the file content as text or bytes based on `binary`
261+
:rtype: str or bytes
262+
"""
263+
223264
r = requests.get(fetch_uri, headers=self.request_headers)
224265
if binary:
225266
return r.content
226267
return r.text
227268

228269
def log(self, level, message):
270+
"""log a message with defined log level
271+
272+
:param level: must be a valid logging log level (debug, info, warning, error)
273+
:type level: str
274+
:param message: the message to log
275+
:type message: str
276+
"""
277+
229278
if level == "debug":
230279
logging.debug(message)
231280
elif level == "info":
@@ -236,6 +285,11 @@ def log(self, level, message):
236285
logging.error(message)
237286

238287
def health_check(self):
288+
"""submit an example request to the OpenCTI API.
289+
290+
:return: returns `True` if the health check has been successful
291+
:rtype: bool
292+
"""
239293
try:
240294
test = self.threat_actor.list(first=1)
241295
if test is not None:
@@ -245,6 +299,12 @@ def health_check(self):
245299
return False
246300

247301
def get_logs_worker_config(self):
302+
"""get the logsWorkerConfig
303+
304+
return: the logsWorkerConfig
305+
rtype: dict
306+
"""
307+
248308
logging.info("Getting logs worker config...")
249309
query = """
250310
query LogsWorkerConfig {
@@ -259,6 +319,14 @@ def get_logs_worker_config(self):
259319
return result["data"]["logsWorkerConfig"]
260320

261321
def not_empty(self, value):
322+
"""check if a value is empty for str, list and int
323+
324+
:param value: value to check
325+
:type value: str or list or int
326+
:return: returns `True` if the value is one of the supported types and not empty
327+
:rtype: bool
328+
"""
329+
262330
if value is not None:
263331
if isinstance(value, str):
264332
if len(value) > 0:
@@ -279,6 +347,16 @@ def not_empty(self, value):
279347
return False
280348

281349
def process_multiple(self, data, with_pagination=False):
350+
"""processes data returned by the OpenCTI API with multiple entities
351+
352+
:param data: data to process
353+
:type data:
354+
:param with_pagination: whether to use pagination with the API, defaults to False
355+
:type with_pagination: bool, optional
356+
:return: returns either a dict or list with the processes entities
357+
:rtype: list or dict
358+
"""
359+
282360
if with_pagination:
283361
result = {"entities": [], "pagination": {}}
284362
else:
@@ -306,6 +384,14 @@ def process_multiple(self, data, with_pagination=False):
306384
return result
307385

308386
def process_multiple_ids(self, data):
387+
"""processes data returned by the OpenCTI API with multiple ids
388+
389+
:param data: data to process
390+
:type data:
391+
:return: returns a list of ids
392+
:rtype: list
393+
"""
394+
309395
result = []
310396
if data is None:
311397
return result
@@ -316,6 +402,14 @@ def process_multiple_ids(self, data):
316402
return result
317403

318404
def process_multiple_fields(self, data):
405+
"""processes data returned by the OpenCTI API with multiple fields
406+
407+
:param data: data to process
408+
:type data: dict
409+
:return: returns the data dict with all fields processed
410+
:rtype: dict
411+
"""
412+
319413
if data is None:
320414
return data
321415
if (
@@ -386,6 +480,13 @@ def process_multiple_fields(self, data):
386480
return data
387481

388482
def upload_file(self, **kwargs):
483+
"""upload a file to OpenCTI API
484+
485+
:param `**kwargs`: arguments for file upload (required: `file_name` and `data`)
486+
:return: returns the query respons for the file upload
487+
:rtype: dict
488+
"""
489+
389490
file_name = kwargs.get("file_name", None)
390491
data = kwargs.get("data", None)
391492
mime_type = kwargs.get("mime_type", "text/plain")
@@ -462,6 +563,18 @@ def create_vulnerability_if_not_exists(
462563
)
463564

464565
def resolve_role(self, relation_type, from_type, to_type):
566+
"""resolves the role for a specified entity
567+
568+
:param relation_type: input relation type
569+
:type relation_type: str
570+
:param from_type: entity type
571+
:type from_type: str
572+
:param to_type: entity type
573+
:type to_type: str
574+
:return: returns the role mapping
575+
:rtype: dict
576+
"""
577+
465578
if from_type == "stix-relation":
466579
from_type = "stix_relation"
467580
if to_type == "stix-relation":

pycti/api/opencti_api_connector.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
import json
22
import logging
33

4+
from typing import Dict, Any
5+
46
from pycti.connector.opencti_connector import OpenCTIConnector
57

68

79
class OpenCTIApiConnector:
10+
"""OpenCTIApiConnector
11+
"""
12+
813
def __init__(self, api):
914
self.api = api
1015

11-
def list(self):
16+
def list(self) -> Dict:
17+
"""list available connectors
18+
19+
:return: return dict with connectors
20+
:rtype: dict
21+
"""
22+
1223
logging.info("Getting connectors ...")
1324
query = """
1425
query GetConnectors {
@@ -26,7 +37,17 @@ def list(self):
2637
result = self.api.query(query)
2738
return result["data"]["connectors"]
2839

29-
def ping(self, connector_id: str, connector_state) -> None:
40+
def ping(self, connector_id: str, connector_state: Any) -> Dict:
41+
"""pings a connector by id and state
42+
43+
:param connector_id: the connectors id
44+
:type connector_id: str
45+
:param connector_state: state for the connector
46+
:type connector_state:
47+
:return: the response pingConnector data dict
48+
:rtype: dict
49+
"""
50+
3051
query = """
3152
mutation PingConnector($id: ID!, $state: String) {
3253
pingConnector(id: $id, state: $state) {
@@ -40,7 +61,15 @@ def ping(self, connector_id: str, connector_state) -> None:
4061
)
4162
return result["data"]["pingConnector"]
4263

43-
def register(self, connector: OpenCTIConnector):
64+
def register(self, connector: OpenCTIConnector) -> Dict:
65+
"""register a connector with OpenCTI
66+
67+
:param connector: `OpenCTIConnector` connector object
68+
:type connector: OpenCTIConnector
69+
:return: the response registerConnector data dict
70+
:rtype: dict
71+
"""
72+
4473
query = """
4574
mutation RegisterConnector($input: RegisterConnectorInput) {
4675
registerConnector(input: $input) {

pycti/api/opencti_api_job.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
import logging
22

3+
from typing import List
4+
35

46
class OpenCTIApiJob:
7+
"""OpenCTIApiJob
8+
"""
9+
510
def __init__(self, api):
611
self.api = api
712

8-
def update_job(self, job_id: str, status: str, messages: [str]):
13+
def update_job(self, job_id: str, status: str, messages: List[str]) -> str:
14+
"""update a job with the API
15+
16+
:param job_id: job id
17+
:type job_id: str
18+
:param status: job status
19+
:type status: str
20+
:param messages: job messages
21+
:type messages: list
22+
:return: the id for the updateJob
23+
:rtype: str
24+
"""
25+
926
logging.info("Reporting job " + job_id + " with status " + status + "...")
1027
query = """
1128
mutation UpdateJob($id: ID!, $status: Status!, $messages: [String]) {
@@ -19,7 +36,15 @@ def update_job(self, job_id: str, status: str, messages: [str]):
1936
)
2037
return result["data"]["updateJob"]["internal_id_key"]
2138

22-
def initiate_job(self, work_id: str):
39+
def initiate_job(self, work_id: str) -> str:
40+
"""initiate a job with the API
41+
42+
:param work_id: id for the job
43+
:type work_id: str
44+
:return: the id for the initiateJob
45+
:rtype: str
46+
"""
47+
2348
logging.info("Creating new job on work " + work_id)
2449
query = """
2550
mutation InitiateJob($id: ID!) {

0 commit comments

Comments
 (0)