Skip to content

Commit 33d4140

Browse files
committed
Implement chunking algorithm
This commit adds a chunking algorithm which will split payloads based on the maximum allowed size and length of data for the Nightfall API.
1 parent a669201 commit 33d4140

File tree

5 files changed

+107
-25
lines changed

5 files changed

+107
-25
lines changed

CHANGELOG

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ Changelog
33

44
Here you can see the full list of changes between each Nightfall release.
55

6+
Version 0.3.0
7+
-------------
8+
9+
Released on June 13, 2021
10+
11+
- Implement basic chunking algorithm to split payloads per the API limts.
12+
613
Version 0.2.0
714
-------------
815

docs/api.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
API
22
===
33

4-
.. note::
5-
Unless otherwise noted all arguments are of the :class:`str` type.
6-
74
API Object
85
----------
96

nightfall/api.py

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,18 @@
1111
import logging
1212

1313
class Api():
14-
"""A python interface into the Nightfall API"""
14+
"""A python interface for the Nightfall API.
15+
16+
.. data:: MAX_PAYLOAD_SIZE
17+
18+
Maximum payload size that the Nightfall API will accept
19+
20+
.. data:: MAX_NUM_ITEMS
21+
22+
Maximum number of items that the Nightfall API will accept
23+
"""
24+
MAX_PAYLOAD_SIZE = 450_000
25+
MAX_NUM_ITEMS = 50_000
1526

1627
def __init__(self, token, condition_set):
1728
"""Instantiate a new nightfall.Api object.
@@ -27,26 +38,73 @@ def __init__(self, token, condition_set):
2738
'x-api-key': self.token
2839
}
2940

41+
def make_payloads(self, data):
42+
"""Turn a list of strings into a list of acceptable payloads.
43+
44+
Creates chunks based on the MAX_PAYLOAD_SIZE and MAX_NUM_ITEMS
45+
constants.
46+
47+
:param data: list of string
48+
:returns: list of list of strings
49+
"""
50+
cur_chunk_bytes = 0
51+
cur_chunk = []
52+
chunks = []
53+
54+
for i in data:
55+
if cur_chunk_bytes + len(i) >= self.MAX_PAYLOAD_SIZE or \
56+
len(cur_chunk) >= self.MAX_NUM_ITEMS:
57+
chunks.append(cur_chunk)
58+
59+
cur_chunk_bytes = len(i)
60+
61+
if len(i) < self.MAX_PAYLOAD_SIZE:
62+
cur_chunk = [i]
63+
else:
64+
cur_chunk = []
65+
for i in range(0, len(i), self.MAX_PAYLOAD_SIZE):
66+
chunks.append([i[i:i+self.MAX_PAYLOAD_SIZE]])
67+
68+
else:
69+
cur_chunk.append(i)
70+
cur_chunk_bytes += len(i)
71+
if cur_chunk:
72+
chunks.append(cur_chunk)
73+
74+
return chunks
75+
76+
3077
def scan(self, data):
31-
"""Scan a piece of data with Nightfall.
78+
"""Scan lists of data with Nightfall.
79+
80+
This method will convert the list of strings into chunks if necessary
81+
and then makes one or more requests to the Nightfall API to scan the
82+
data.
3283
33-
:param data: Data to scan.
84+
:param data: list of strings to scan.
3485
:type data: list
86+
87+
:returns: list of list of resposes for items in payload.
3588
"""
36-
payload = {
37-
'payload': data,
38-
'config': {
39-
'conditionSetUUID': self.condition_set
89+
responses = []
90+
91+
for i in self.make_payloads(data):
92+
payload = {
93+
'payload': i,
94+
'config': {
95+
'conditionSetUUID': self.condition_set
96+
}
4097
}
41-
}
4298

43-
response = requests.post(
44-
url=self._url,
45-
headers=self._headers,
46-
data=json.dumps(payload)
47-
)
48-
response.raise_for_status()
99+
response = requests.post(
100+
url=self._url,
101+
headers=self._headers,
102+
data=json.dumps(payload)
103+
)
104+
response.raise_for_status()
105+
responses += response.json()
49106

50-
return response
107+
return responses
108+
51109

52110

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def readme():
1616

1717
setup(
1818
name="nightfall",
19-
version="0.2.0",
19+
version="0.3.0",
2020
description="Python SDK for Nightfall",
2121
long_description=readme(),
2222
long_description_content_type="text/markdown",

tests/nightfall/test_api.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,32 @@ def setUp(self):
1313
os.getenv('NIGHTFALL_CONDITION_SET')
1414
)
1515

16-
def testBadScan(self):
17-
with self.assertRaises(requests.exceptions.HTTPError) as e:
18-
resp = self.client.scan('')
19-
2016
def testScan(self):
21-
"""Test basics of API, can submit a request and receive a response."""
17+
"""Test basics of API, can submit a request and receive a response.
18+
19+
This test assumes that you don't have a condition set matching the 'testing' string.
20+
"""
2221
resp = self.client.scan(['testing'])
23-
self.assertEqual(resp.status_code, 200)
22+
self.assertEqual(resp[0], None)
23+
24+
def testChunking(self):
25+
"""Test chunking algorithm."""
26+
27+
# a list of 10 strings that are 100k bytes each should turn into a
28+
# list of three lists
29+
large_list = []
30+
large_string = "x" * 100000
31+
32+
for i in range(0,10):
33+
large_list.append(large_string)
2434

35+
chunks = self.client.make_payloads(large_list)
36+
self.assertEqual(len(chunks), 3)
37+
38+
# a list of 100,000 single byte items should turn into two lists
39+
many_list = []
40+
for i in range(0, 100000):
41+
many_list.append('x')
42+
43+
chunks = self.client.make_payloads(many_list)
44+
self.assertEqual(len(chunks), 2)

0 commit comments

Comments
 (0)