Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@ on:
push:
paths:
- '**.py'
branches:
- main
pull_request:
paths:
- '**.py'
branches:
- main

jobs:
analyze:
Expand Down
18 changes: 14 additions & 4 deletions functions/hello/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
"""Main module for the hello function handler."""

from crowdstrike.foundry.function import Function, Request, Response, APIError

func = Function.instance()
FUNC = Function.instance()


# Handler hello
@func.handler(method="POST", path="/hello")
@FUNC.handler(method="POST", path="/hello")
def on_post(request: Request) -> Response:
"""
Handle POST requests to /hello endpoint.

Args:
request: The incoming request object containing the request body.

Returns:
Response: JSON response with greeting or error message.
"""
#
# Replace the following example code with your handler code
#
Expand All @@ -29,4 +39,4 @@ def on_post(request: Request) -> Response:


if __name__ == "__main__":
func.run()
FUNC.run()
22 changes: 15 additions & 7 deletions functions/hello/test_main.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,50 @@
"""Test module for the hello function handler."""

import importlib
import unittest
from unittest.mock import patch

from crowdstrike.foundry.function import Request

import main


def mock_handler(*_args, **_kwargs):
"""Mock handler decorator for testing."""

def mock_handler(*args, **kwargs):
def identity(func):
return func

return identity


class FnTestCase(unittest.TestCase):
"""Test case class for function handler tests."""

def setUp(self):
"""Set up test fixtures before each test method."""
patcher = patch("crowdstrike.foundry.function.Function.handler", new=mock_handler)
self.addCleanup(patcher.stop)
self.handler_patch = patcher.start()

import importlib
import main
importlib.reload(main)

def test_on_post_success(self):
from main import on_post
"""Test successful POST request with valid name in body."""
request = Request()
request.body = {
"name": "Test User"
}

response = on_post(request)
response = main.on_post(request)
self.assertEqual(response.code, 200)
self.assertEqual(response.body["greeting"], "Hello Test User! It is nice to see you.")

def test_on_post_missing_name(self):
from main import on_post
"""Test POST request with missing name in body returns error."""
request = Request()

response = on_post(request)
response = main.on_post(request)
self.assertEqual(response.code, 400)
self.assertEqual(len(response.errors), 1)
self.assertEqual(response.errors[0].message, "missing name from request body")
Expand Down
17 changes: 14 additions & 3 deletions functions/host-details/main.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
"""Main module for the host-details function handler."""

from crowdstrike.foundry.function import Function, Request, Response, APIError
# Import service collection you'd like to use
from falconpy import Hosts

func = Function.instance()
FUNC = Function.instance()


@func.handler(method="POST", path="/host-details")
@FUNC.handler(method="POST", path="/host-details")
def on_post(request: Request) -> Response:
"""
Handle POST requests to /host-details endpoint.

Args:
request: The incoming request object containing the request body.

Returns:
Response: JSON response with host details or error message.
"""
# Validate request
if "host_id" not in request.body:
return Response(
Expand Down Expand Up @@ -37,4 +48,4 @@ def on_post(request: Request) -> Response:


if __name__ == "__main__":
func.run()
FUNC.run()
28 changes: 17 additions & 11 deletions functions/host-details/test_main.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
"""Test module for the host-details function handler."""

import importlib
import unittest
from unittest.mock import patch, MagicMock

from crowdstrike.foundry.function import Request

import main


def mock_handler(*_args, **_kwargs):
"""Mock handler decorator for testing."""

def mock_handler(*args, **kwargs):
def identity(func):
return func

return identity


class FnTestCase(unittest.TestCase):
"""Test case class for function handler tests."""

def setUp(self):
"""Set up test fixtures before each test method."""
patcher = patch("crowdstrike.foundry.function.Function.handler", new=mock_handler)
self.addCleanup(patcher.stop)
self.handler_patch = patcher.start()

import importlib
import main
importlib.reload(main)

@patch("main.Hosts")
def test_on_post_success(self, mock_hosts_class):
from main import on_post

"""Test successful POST request with valid host_id in body."""
# Mock the Hosts instance and its response
mock_hosts_instance = MagicMock()
mock_hosts_class.return_value = mock_hosts_instance
Expand All @@ -44,27 +51,26 @@ def test_on_post_success(self, mock_hosts_class):
"host_id": "test-host-123"
}

response = on_post(request)
response = main.on_post(request)

self.assertEqual(response.code, 200)
self.assertIn("host_details", response.body)
self.assertEqual(response.body["host_details"]["device_id"], "test-host-123")
mock_hosts_instance.get_device_details.assert_called_once_with(ids="test-host-123")

def test_on_post_missing_host_id(self):
from main import on_post
"""Test POST request with missing host_id in body returns error."""
request = Request()

response = on_post(request)
response = main.on_post(request)

self.assertEqual(response.code, 400)
self.assertEqual(len(response.errors), 1)
self.assertEqual(response.errors[0].message, "missing host_id from request body")

@patch("main.Hosts")
def test_on_post_api_error(self, mock_hosts_class):
from main import on_post

"""Test POST request when API returns an error."""
# Mock the Hosts instance to return an error
mock_hosts_instance = MagicMock()
mock_hosts_class.return_value = mock_hosts_instance
Expand All @@ -78,7 +84,7 @@ def test_on_post_api_error(self, mock_hosts_class):
"host_id": "nonexistent-host"
}

response = on_post(request)
response = main.on_post(request)

self.assertEqual(response.code, 404)
self.assertEqual(len(response.errors), 1)
Expand Down
23 changes: 18 additions & 5 deletions functions/host-info/main.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
"""Main module for the host-info function handler."""

from logging import Logger
from typing import Dict
from typing import Dict, Optional

from crowdstrike.foundry.function import Function, Request, Response

from utils import validate_host_id, format_error_response

func = Function.instance()
FUNC = Function.instance()


@FUNC.handler(method="POST", path="/host-info")
def on_post(request: Request, _config: Optional[Dict[str, object]], logger: Logger) -> Response:
"""
Handle POST requests to /host-info endpoint.

Args:
request: The incoming request object containing the request body.
_config: Configuration dictionary (unused).
logger: Logger instance for logging.

@func.handler(method="POST", path="/host-info")
def on_post(request: Request, config: Dict[str, object] | None, logger: Logger) -> Response:
Returns:
Response: JSON response with host info or error message.
"""
host_id = request.body.get("host_id")

logger.info(f"Host ID: {host_id}")
Expand All @@ -27,4 +40,4 @@ def on_post(request: Request, config: Dict[str, object] | None, logger: Logger)


if __name__ == "__main__":
func.run()
FUNC.run()
41 changes: 22 additions & 19 deletions functions/host-info/test_main.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
"""Test module for the host-info function handler."""

import importlib
import unittest
from unittest.mock import patch, MagicMock

from crowdstrike.foundry.function import Request

import main


def mock_handler(*_args, **_kwargs):
"""Mock handler decorator for testing."""

def mock_handler(*args, **kwargs):
def identity(func):
return func

return identity


class FnTestCase(unittest.TestCase):
"""Test case class for function handler tests."""

def setUp(self):
"""Set up test fixtures before each test method."""
patcher = patch("crowdstrike.foundry.function.Function.handler", new=mock_handler)
self.addCleanup(patcher.stop)
self.handler_patch = patcher.start()

import importlib
import main
importlib.reload(main)

@patch("main.validate_host_id")
@patch("main.format_error_response")
def test_on_post_success(self, mock_format_error, mock_validate_host_id):
from main import on_post

"""Test successful POST request with valid host_id in body."""
# Mock validation to return True for valid host ID
mock_validate_host_id.return_value = True

Expand All @@ -37,7 +44,7 @@ def test_on_post_success(self, mock_format_error, mock_validate_host_id):
"host_id": "valid-host-123"
}

response = on_post(request, config=None, logger=mock_logger)
response = main.on_post(request, _config=None, logger=mock_logger)

self.assertEqual(response.code, 200)
self.assertEqual(response.body["host"], "valid-host-123")
Expand All @@ -57,8 +64,7 @@ def test_on_post_success(self, mock_format_error, mock_validate_host_id):
@patch("main.validate_host_id")
@patch("main.format_error_response")
def test_on_post_invalid_host_id(self, mock_format_error, mock_validate_host_id):
from main import on_post

"""Test POST request with invalid host_id returns error."""
# Mock validation to return False for invalid host ID
mock_validate_host_id.return_value = False

Expand All @@ -73,7 +79,7 @@ def test_on_post_invalid_host_id(self, mock_format_error, mock_validate_host_id)
"host_id": "invalid-host"
}

response = on_post(request, config=None, logger=mock_logger)
response = main.on_post(request, _config=None, logger=mock_logger)

# Should return error response (default code is likely 400)
self.assertEqual(response.errors, [{"code": 400, "message": "Invalid host ID format"}])
Expand All @@ -93,8 +99,7 @@ def test_on_post_invalid_host_id(self, mock_format_error, mock_validate_host_id)
@patch("main.validate_host_id")
@patch("main.format_error_response")
def test_on_post_missing_host_id(self, mock_format_error, mock_validate_host_id):
from main import on_post

"""Test POST request with missing host_id returns error."""
# Mock validation to return False for None host ID
mock_validate_host_id.return_value = False

Expand All @@ -107,7 +112,7 @@ def test_on_post_missing_host_id(self, mock_format_error, mock_validate_host_id)
request = Request()
request.body = {} # No host_id provided

response = on_post(request, config=None, logger=mock_logger)
response = main.on_post(request, _config=None, logger=mock_logger)

# Should return error response
self.assertEqual(response.errors, [{"code": 400, "message": "Invalid host ID format"}])
Expand All @@ -127,8 +132,7 @@ def test_on_post_missing_host_id(self, mock_format_error, mock_validate_host_id)
@patch("main.validate_host_id")
@patch("main.format_error_response")
def test_on_post_empty_host_id(self, mock_format_error, mock_validate_host_id):
from main import on_post

"""Test POST request with empty host_id returns error."""
# Mock validation to return False for empty string
mock_validate_host_id.return_value = False

Expand All @@ -143,7 +147,7 @@ def test_on_post_empty_host_id(self, mock_format_error, mock_validate_host_id):
"host_id": ""
}

response = on_post(request, config=None, logger=mock_logger)
response = main.on_post(request, _config=None, logger=mock_logger)

# Should return error response
self.assertEqual(response.errors, [{"code": 400, "message": "Invalid host ID format"}])
Expand All @@ -162,9 +166,8 @@ def test_on_post_empty_host_id(self, mock_format_error, mock_validate_host_id):

@patch("main.validate_host_id")
@patch("main.format_error_response")
def test_on_post_with_config(self, mock_format_error, mock_validate_host_id):
from main import on_post

def test_on_post_with_config(self, _mock_format_error, mock_validate_host_id):
"""Test POST request with config parameter."""
# Mock validation to return True
mock_validate_host_id.return_value = True

Expand All @@ -179,7 +182,7 @@ def test_on_post_with_config(self, mock_format_error, mock_validate_host_id):
"host_id": "test-host-456"
}

response = on_post(request, config=config, logger=mock_logger)
response = main.on_post(request, _config=config, logger=mock_logger)

self.assertEqual(response.code, 200)
self.assertEqual(response.body["host"], "test-host-456")
Expand Down
2 changes: 2 additions & 0 deletions functions/host-info/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Utility functions for host-info function handler."""

import json
import re
from typing import Dict, Any, Optional
Expand Down
Loading