---
title: 'Setting Up the Core of IntelliSIEM – Structure, TDD, and API Connection'
url: 'https://bsb.perryconsulting.net/blog/setting-up-the-core-of-intellisiem-structure-tdd-and-api-connection'
markdown: 'https://bsb.perryconsulting.net/blog/setting-up-the-core-of-intellisiem-structure-tdd-and-api-connection.md'
date: '2024-09-25'
description: "Part 1 of this series covered the initial tool setup, and testing of the environment. In Part 2, we're going to dive into structure, tests, and API connections. In this post, I’m excited to dive into the development of IntelliSIEM’s data collection module. This step is crucial as it lays the foundation for how we will gather, process, and analyze threat data from various sources. To see this project in its curr"
taxonomy:
  category:
    - projects
  tag:
    - python
    - security
---

# Setting Up the Core of IntelliSIEM – Structure, TDD, and API Connection

 25 SEP 2024  [python](https://bsb.perryconsulting.net/blog/tag:python) [security](https://bsb.perryconsulting.net/blog/tag:security)  

  *Part 1 of this series covered the initial tool setup, and testing of the environment.**In Part 2, we're going to dive into structure, tests, and API connections.*

In this post, I’m excited to dive into the development of IntelliSIEM’s data collection module. This step is crucial as it lays the foundation for how we will gather, process, and analyze threat data from various sources.

To see this project in its current state, head over to my repo and check it out: [IntelliSIEM on GitHub](https://github.com/perryconsulting/IntelliSIEM)

---

### **Step 1: Project Structure and Setup**

To ensure the project remains organized and scalable, I began by designing a clear directory structure with separate folders for configuration, data, source code, and tests:

```
<span class="line"><span class="token">IntelliSIEM/
</span></span><span class="line"><span class="token">           ├── LICENSE
</span></span><span class="line"><span class="token">           ├── README.md
</span></span><span class="line"><span class="token">           ├── config/
</span></span><span class="line"><span class="token">           │         └── config_template.yaml
</span></span><span class="line"><span class="token">           ├── data/
</span></span><span class="line"><span class="token">           ├── src/
</span></span><span class="line"><span class="token">           │      ├── __init__.py
</span></span><span class="line"><span class="token">           │      ├── data_collection.py
</span></span><span class="line"><span class="token">           │      ├── error_handling.py
</span></span><span class="line"><span class="token">           │      └── api_client.py
</span></span><span class="line"><span class="token">           ├── tests/
</span></span><span class="line"><span class="token">           │        ├── __init__.py
</span></span><span class="line"><span class="token">           │        └── test_data_collection.py
</span></span><span class="line"><span class="token">           └── requirements.txt
</span></span>
```

With this design in mind, I created the `config`, `data`, `src`, and `tests` subdirectories in the project directory. In the project settings in PyCharm, I also marked `src` as "Sources", `tests` as "Tests", and `config` and `data` as "Resources". I then added `__init__.py` files to the `src/` and `tests/` directories to make them Python packages, ensuring smooth module imports. Finally, I added `requirements.txt` so that end users can easily import the libraries necessary to run the application.

BASH

```
<span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">mkdir</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">config</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">mkdir</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">data</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">mkdir</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">src</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">mkdir</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">tests</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">touch</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">src/__init__.py</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">touch</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">tests/__init__.py</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">touch</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">requirements.txt</span><span class="token">
</span></span>
```

### **Step 2: README and License**

Before pushing any code to GitHub, I documented the project setup and usage in the `README.md` file and applied the Apache License 2.0 to protect the code and clarify usage rights. Applying the license involves creating the LICENSE file, populating it with the entire license text, and creating a stub placed at the top of every code file. You'll see that stub as we move through the project. To see the full text of either, check out the full project in the [GitHub repo](https://github.com/perryconsulting/IntelliSIEM).

### **Step 3: Credential Handling**

Sensitive information like API keys should never be hard-coded. For this early stage of development, I created a `config/config_template.yaml` file with the basic syntax that we'll build on later to implement a more secure way of handling the keys:

YAML

```
<span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 1</span><span class="token" style="color: #024c1a;;--phiki-dark-color: #72f088">a</span><span class="token" style="color: #024c1a;;--phiki-dark-color: #72f088">pi_keys</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 2</span><span class="token">    </span><span class="token" style="color: #024c1a;;--phiki-dark-color: #72f088">a</span><span class="token" style="color: #024c1a;;--phiki-dark-color: #72f088">lient_vault</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">YOUR_ALIEN_VAULT_API_KEY</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 3</span><span class="token">    </span><span class="token" style="color: #024c1a;;--phiki-dark-color: #72f088">v</span><span class="token" style="color: #024c1a;;--phiki-dark-color: #72f088">irus_total</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">YOUR_VIRUS_TOTAL_API_KEY</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">
</span></span>
```

To prevent the exposure of our own keys, we'll first add an entry to `.gitignore` for `config/config.yaml`. As a reminder, the `.gitignore` should now look something like this:

.gitignore

```
<span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 1</span><span class="token">.venv/
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 2</span><span class="token">.idea/
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 3</span><span class="token">__pychache__/
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 4</span><span class="token">/config/config.yaml
</span></span>
```

Then, copy the template to `config.yaml` and fill in our keys. This will ensure that version control ignores the file to prevent accidental exposure.

BASH

```
<span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">cp</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">config/config_template.yaml</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">config/config.yaml</span><span class="token">
</span></span>
```

### **Step 4: Implementing Test-Driven Development (TDD)**

OK. I can already hear the groans, "Why are we wasting time on this?" or "TDD is too hard!". Yeah, I get it, but the crux of the matter is that test-driven development helps catch issues early. I wrote some initial test cases using `pytest` and `pytest-mock` for the data collection module. First things first, we need to make sure we've added `pytest` to our project. Let's utilize our newly created `requirements.txt` file to make sure we have everything we need so far. Open it up in the editor and it should look like this:

TXT

```
<span class="line"><span class="token">requests
</span></span><span class="line"><span class="token">pandas
</span></span><span class="line"><span class="token">matplotlib
</span></span><span class="line"><span class="token">seaborn
</span></span><span class="line"><span class="token">fpdf
</span></span><span class="line"><span class="token">reportlab
</span></span><span class="line"><span class="token">json5
</span></span><span class="line"><span class="token">pyyaml
</span></span><span class="line"><span class="token">pytest
</span></span><span class="line"><span class="token">pytest-mock
</span></span><span class="line"><span class="token">beautifulsoup4
</span></span><span class="line"><span class="token">lxml
</span></span><span class="line"><span class="token">sqlalchemy
</span></span>
```

Save that and open the console again and we'll test it:

BASH

```
<span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">pip</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">install</span><span class="token"> </span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">-</span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">r</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">requirements.txt</span><span class="token">
</span></span>
```

Now that we have all our libraries installed, we're going to create our first tests. Remember, in TDD we always expect the tests to fail first. Here’s a sample of the tests I wrote in `tests/test_data_collection.py`:

PYTHON

```
<span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 1</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  IntelliSIEM Copyright 2024, Rob Perry</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 2</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 3</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Licensed under the Apache License, Version 2.0 (the "License");</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 4</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  you may not use this file except in compliance with the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 5</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  You may obtain a copy of the License at</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 6</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 7</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">      http://www.apache.org/licenses/LICENSE-2.0</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 8</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 9</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Unless required by applicable law or agreed to in writing, software</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">10</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  distributed under the License is distributed on an "AS IS" BASIS,</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">11</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">12</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  See the License for the specific language governing permissions and</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">13</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  limitations under the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">14</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">15</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">from</span><span class="token"> </span><span class="token">src</span><span class="token">.</span><span class="token">data_collection</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">import</span><span class="token"> </span><span class="token">fetch_threat_data</span><span class="token">,</span><span class="token"> </span><span class="token">APIError</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">16</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">17</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">def</span><span class="token"> </span><span class="token" style="color: #622cbc;;--phiki-dark-color: #dbb7ff">test_fetch_threat_data_success</span><span class="token">(</span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">mocker</span><span class="token">)</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">18</span><span class="token">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">19</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    Test fetching threat data with valid API keys.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">20</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">21</span><span class="token">    </span><span class="token">mocker</span><span class="token">.</span><span class="token">patch</span><span class="token">(</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">src.api_client.APIClient.get_data</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">return_value</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token">[</span><span class="token">{</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">id</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">test</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">}</span><span class="token">]</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">22</span><span class="token">    </span><span class="token">threats</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">fetch_threat_data</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">23</span><span class="token">    </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">assert</span><span class="token"> </span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">len</span><span class="token">(</span><span class="token">threats</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">></span><span class="token"> </span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">0</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">24</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">25</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">def</span><span class="token"> </span><span class="token" style="color: #622cbc;;--phiki-dark-color: #dbb7ff">test_fetch_threat_data_failure</span><span class="token">(</span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">mocker</span><span class="token">)</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">26</span><span class="token">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">27</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    Test fetching threat data with invalid API keys.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">28</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">29</span><span class="token">    </span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc"> Mock API client responses to raise an error</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">30</span><span class="token">    </span><span class="token">mocker</span><span class="token">.</span><span class="token">patch</span><span class="token">(</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">src.api_client.APIClient.get_data</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">side_effect</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token">APIError</span><span class="token">(</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">API Error</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">)</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">31</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">32</span><span class="token">    </span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc"> Expect the APIError exception to be raised and handle it in the function</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">33</span><span class="token">    </span><span class="token">threats</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">fetch_threat_data</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">34</span><span class="token">    </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">assert</span><span class="token"> </span><span class="token">threats</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">==</span><span class="token"> </span><span class="token">[</span><span class="token">]</span><span class="token">  </span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc"> Expecting an empty list as a fallback</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">35</span><span class="token">    
</span></span>
```

Now, as I said, we anticipate that this is going to fail and we can verify that by running our first tests.

BASH

```
<span class="line"><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">pytest</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">/tests</span><span class="token">
</span></span>
```

Congratulations, you've failed your first test! Now, we can move on to the implementation and start passing. To keep this brief, I'm going to assume that you are consistently running your tests every time you make a change until all your tests pass, so I won't keep covering that aspect repeatedly.

### **Step 5: Implementing the Data Collection Module**

The data collection module is the heart of IntelliSIEM. It fetches data from various threat intelligence APIs. Following our tests logically, we're first importing from `src.data_collection`, but we haven't created that file yet. Let's do that now in `src/data_collection.py`:

PYTHON

```
<span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 1</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  IntelliSIEM Copyright 2024, Rob Perry</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 2</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 3</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Licensed under the Apache License, Version 2.0 (the "License");</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 4</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  you may not use this file except in compliance with the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 5</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  You may obtain a copy of the License at</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 6</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 7</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">      http://www.apache.org/licenses/LICENSE-2.0</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 8</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 9</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Unless required by applicable law or agreed to in writing, software</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">10</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  distributed under the License is distributed on an "AS IS" BASIS,</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">11</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">12</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  See the License for the specific language governing permissions and</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">13</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  limitations under the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">14</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">15</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">from</span><span class="token"> </span><span class="token">src</span><span class="token">.</span><span class="token">api_client</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">import</span><span class="token"> </span><span class="token">APIClient</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">16</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">from</span><span class="token"> </span><span class="token">src</span><span class="token">.</span><span class="token">error_handling</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">import</span><span class="token"> </span><span class="token">APIError</span><span class="token">,</span><span class="token"> </span><span class="token">log_error</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">17</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">from</span><span class="token"> </span><span class="token">src</span><span class="token">.</span><span class="token">config</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">import</span><span class="token"> </span><span class="token">load_config</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">18</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">19</span><span class="token">config</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">load_config</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">20</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">21</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">def</span><span class="token"> </span><span class="token" style="color: #622cbc;;--phiki-dark-color: #dbb7ff">fetch_threat_data</span><span class="token">(</span><span class="token">)</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">22</span><span class="token">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">23</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    Fetch threat data from various sources and return the combined result.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">24</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">25</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    :return: (list) A list of threat intelligence data.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">26</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">27</span><span class="token">    </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">try</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">28</span><span class="token">        </span><span class="token">alien_vault</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">APIClient</span><span class="token">(</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">https://otx.alienvault.com/api/v1</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">29</span><span class="token">                                </span><span class="token">{</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">X-OTX-API-KEY</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">:</span><span class="token"> </span><span class="token">config</span><span class="token">[</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">api_keys</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">]</span><span class="token">[</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">alien_vault</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">]</span><span class="token">}</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">30</span><span class="token">        </span><span class="token">vt</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">APIClient</span><span class="token">(</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">https://www.virustotal.com/api/v3</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">,</span><span class="token"> </span><span class="token">{</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">x-apikey</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">:</span><span class="token"> </span><span class="token">config</span><span class="token">[</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">api_keys</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">]</span><span class="token">[</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">virus_total</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">]</span><span class="token">}</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">31</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">32</span><span class="token">        </span><span class="token">alien_vault_data</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">alien_vault</span><span class="token">.</span><span class="token">get_data</span><span class="token">(</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">indicators/export</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">33</span><span class="token">        </span><span class="token">vt_data</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">vt</span><span class="token">.</span><span class="token">get_data</span><span class="token">(</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">files</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">params</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token">{</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">limit</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">10</span><span class="token">}</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">34</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">35</span><span class="token">        </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">return</span><span class="token"> </span><span class="token">alien_vault_data</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">+</span><span class="token"> </span><span class="token">vt_data</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">36</span><span class="token">    </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">except</span><span class="token"> </span><span class="token">APIError</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">as</span><span class="token"> </span><span class="token">e</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">37</span><span class="token">        </span><span class="token">log_error</span><span class="token">(</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">f</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">Failed to fetch threat data: </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">{</span><span class="token">e</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">}</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">38</span><span class="token">        </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">return</span><span class="token"> </span><span class="token">[</span><span class="token">]</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">39</span><span class="token">        
</span></span>
```

Continuing logically, we need to implement the `APIClient` class next. The `APIClient` class manages the API requests and provides error handling. We'll build more robust error handling as we build better tests throughout the project. Here is the current src/api\_client.py:

PYTHON

```
<span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 1</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  IntelliSIEM Copyright 2024, Rob Perry</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 2</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 3</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Licensed under the Apache License, Version 2.0 (the "License");</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 4</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  you may not use this file except in compliance with the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 5</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  You may obtain a copy of the License at</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 6</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 7</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">      http://www.apache.org/licenses/LICENSE-2.0</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 8</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 9</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Unless required by applicable law or agreed to in writing, software</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">10</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  distributed under the License is distributed on an "AS IS" BASIS,</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">11</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">12</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  See the License for the specific language governing permissions and</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">13</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  limitations under the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">14</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">15</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">import</span><span class="token"> </span><span class="token">requests</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">16</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">from</span><span class="token"> </span><span class="token">src</span><span class="token">.</span><span class="token">error_handling</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">import</span><span class="token"> </span><span class="token">APIError</span><span class="token">,</span><span class="token"> </span><span class="token">log_error</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">17</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">18</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">class</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">APIClient</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">19</span><span class="token">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">20</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    A client for interacting with various threat intelligence APIs.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">21</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">22</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">23</span><span class="token">    </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">def</span><span class="token"> </span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">__init__</span><span class="token">(</span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">self</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">base_url</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">headers</span><span class="token">)</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">24</span><span class="token">        </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">25</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        Initialize the API client with the base URL and headers.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">26</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">27</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        :param base_url: (str) The base URL of the API.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">28</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        :param headers: (dict) Headers required for API requests.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">29</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">30</span><span class="token">        </span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">self</span><span class="token">.</span><span class="token">base_url</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">base_url</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">31</span><span class="token">        </span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">self</span><span class="token">.</span><span class="token">headers</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">headers</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">32</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">33</span><span class="token">    </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">def</span><span class="token"> </span><span class="token" style="color: #622cbc;;--phiki-dark-color: #dbb7ff">get_data</span><span class="token">(</span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">self</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">endpoint</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">params</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">None</span><span class="token">)</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">34</span><span class="token">        </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">35</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        Fetch data from the specified endpoint.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">36</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">37</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        :param endpoint: (str) The API endpoint to fetch data from.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">38</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        :param params: (dict) Optional query parameters.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">39</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        :return: (dict) JSON response from the API.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">40</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        :raises APIError: If there is an error with the API request.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">41</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">        </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">42</span><span class="token">        </span><span class="token">url</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">f</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">{</span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">self</span><span class="token">.</span><span class="token">base_url</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">}</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">/</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">{</span><span class="token">endpoint</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">}</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">43</span><span class="token">        </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">try</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">44</span><span class="token">            </span><span class="token">response</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token"> </span><span class="token">requests</span><span class="token">.</span><span class="token">get</span><span class="token">(</span><span class="token">url</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">headers</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">self</span><span class="token">.</span><span class="token">headers</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">params</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token">params</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">timeout</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">10</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">45</span><span class="token">            </span><span class="token">response</span><span class="token">.</span><span class="token">raise_for_status</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">46</span><span class="token">            </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">return</span><span class="token"> </span><span class="token">response</span><span class="token">.</span><span class="token">json</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">47</span><span class="token">        </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">except</span><span class="token"> </span><span class="token">requests</span><span class="token">.</span><span class="token">exceptions</span><span class="token">.</span><span class="token">RequestException</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">as</span><span class="token"> </span><span class="token">e</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">48</span><span class="token">            </span><span class="token">log_error</span><span class="token">(</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">f</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">API request failed: </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">{</span><span class="token">e</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">}</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">49</span><span class="token">            </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">raise</span><span class="token"> </span><span class="token">APIError</span><span class="token">(</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">f</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">API request failed: </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">{</span><span class="token">e</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">}</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">50</span><span class="token">            
</span></span>
```

I'm going to touch on error handling in Step 6, so before we move on to that, let's ensure that we've implementing loading the API keys from the `config.yaml` file. We take care of that in `config.py`:

PYTHON

```
<span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 1</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  IntelliSIEM Copyright 2024, Rob Perry</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 2</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 3</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Licensed under the Apache License, Version 2.0 (the "License");</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 4</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  you may not use this file except in compliance with the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 5</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  You may obtain a copy of the License at</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 6</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 7</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">      http://www.apache.org/licenses/LICENSE-2.0</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 8</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 9</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Unless required by applicable law or agreed to in writing, software</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">10</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  distributed under the License is distributed on an "AS IS" BASIS,</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">11</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">12</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  See the License for the specific language governing permissions and</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">13</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  limitations under the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">14</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">15</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">import</span><span class="token"> </span><span class="token">yaml</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">16</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">17</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">def</span><span class="token"> </span><span class="token" style="color: #622cbc;;--phiki-dark-color: #dbb7ff">load_config</span><span class="token">(</span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">config_file</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">config/config.yaml</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">)</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">18</span><span class="token">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">19</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    Load configuration from the specified YAML file.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">20</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">21</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    :param config_file: The path to the YAML configuration file.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">22</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    :return: dict: Configuration data.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">23</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">24</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">25</span><span class="token">    </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">with</span><span class="token"> </span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">open</span><span class="token">(</span><span class="token">config_file</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">r</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">as</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">file</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">26</span><span class="token">        </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">return</span><span class="token"> </span><span class="token">yaml</span><span class="token">.</span><span class="token">safe_load</span><span class="token">(</span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">file</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">27</span><span class="token">        
</span></span>
```

### **Step 6: Error Handling and Logging**

To ensure smooth operation and easier debugging, I implemented a custom `APIError` class and a logging mechanism. Is it robust? Well, not really. As we work on really making this module more robust, we'll improve this class and enhance the error handling and reporting. For now, we're really interested in logging errors so that we can make sure we handle them gracefully when we reach a release-ready version. Here is the current `src/error_handling.py`:

PYTHON

```
<span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 1</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  IntelliSIEM Copyright 2024, Rob Perry</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 2</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 3</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Licensed under the Apache License, Version 2.0 (the "License");</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 4</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  you may not use this file except in compliance with the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 5</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  You may obtain a copy of the License at</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 6</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 7</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">      http://www.apache.org/licenses/LICENSE-2.0</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 8</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none"> 9</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  Unless required by applicable law or agreed to in writing, software</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">10</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  distributed under the License is distributed on an "AS IS" BASIS,</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">11</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">12</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  See the License for the specific language governing permissions and</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">13</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">#</span><span class="token" style="color: #66707b;;--phiki-dark-color: #bdc4cc">  limitations under the License.</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">14</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">15</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">import</span><span class="token"> </span><span class="token">logging</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">16</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">17</span><span class="token">logging</span><span class="token">.</span><span class="token">basicConfig</span><span class="token">(</span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">level</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token">logging</span><span class="token">.</span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">ERROR</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">filename</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">data/error.log</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">format</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">=</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">%(asctime)s</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff"> </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">%(levelname)s</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">: </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">%(message)s</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">'</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">18</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">19</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">class</span><span class="token"> </span><span class="token" style="color: #702c00;;--phiki-dark-color: #ffb757">APIError</span><span class="token">(</span><span class="token" style="color: #023b95;;--phiki-dark-color: #91cbff">Exception</span><span class="token">)</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">20</span><span class="token">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">21</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    Custom exception for API errors.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">22</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">23</span><span class="token">    </span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">pass</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">24</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">25</span><span class="token" style="color: #a0111f;;--phiki-dark-color: #ff9492">def</span><span class="token"> </span><span class="token" style="color: #622cbc;;--phiki-dark-color: #dbb7ff">log_error</span><span class="token">(</span><span class="token" style="color: #0e1116;;--phiki-dark-color: #f0f3f6">message</span><span class="token">)</span><span class="token">:</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">26</span><span class="token">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">27</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    Log error messages to a file.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">28</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">29</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    :param message: (str) The error message to log.
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">30</span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">    </span><span class="token" style="color: #032563;;--phiki-dark-color: #addcff">"""</span><span class="token">
</span></span><span class="line"><span class="line-number" style="color: #88929d;-webkit-user-select: none">31</span><span class="token">    </span><span class="token">logging</span><span class="token">.</span><span class="token">error</span><span class="token">(</span><span class="token">message</span><span class="token">)</span><span class="token">
</span></span>
```

This helps track any issues that arise during API calls or data processing.

Hopefully, all your tests have passed. We have the basic implementation of the data collector, but currently, our tests only cover the basic success and failure states of the collector. In the next post, we will improve and enhance our tests. Once we have robust tests in place, we'll move into better error handling and then revisit the API key handling.

---

## Navigation

- Parent: [Blog](https://bsb.perryconsulting.net/blog.md)
- Previous: [Implementing TDD in IntelliSIEM: Handling Edge Cases and Errors](https://bsb.perryconsulting.net/blog/strengthening-the-foundation-with-comprehensive-testing-in-intellisiem.md)
- Next: [Getting Started with IntelliSIEM: Setting Up the Development Environment](https://bsb.perryconsulting.net/blog/getting-started-with-intellisiem-setting-up-the-development-environment.md)
