Skip to content

Commit

Permalink
remove api key, rename to endpoint_url
Browse files Browse the repository at this point in the history
  • Loading branch information
aviaviavi committed Dec 29, 2024
1 parent ed5a2bc commit a404807
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 95 deletions.
74 changes: 36 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Scarf-py
# scarf-py

Python bindings for [Scarf](https://scarf.sh) telemetry. This package provides a simple and ergonomic way to send telemetry events to Scarf.
A Python client for sending telemetry events to Scarf.

## Installation

Expand All @@ -13,57 +13,55 @@ pip install scarf
```python
from scarf import ScarfEventLogger

# Initialize the logger with your API key and optional timeout
# Initialize with required endpoint URL
logger = ScarfEventLogger(
api_key="your-api-key",
base_url="https://your-scarf-endpoint.com/your-endpoint-id",
timeout=3.0 # Optional: default timeout of 3 seconds
endpoint_url="https://your-scarf-endpoint.com",
timeout=5.0 # Optional: Set default timeout in seconds (default: 3.0)
)

# Log an event with properties
logger.log_event({
# Send an event with properties
success = logger.log_event({
"event": "package_download",
"version": "1.0.0",
"platform": "linux"
"package": "scarf",
"version": "1.0.0"
})

# Log a simple event with no properties
logger.log_event({})

# Log an event with a custom timeout
logger.log_event(
{"event": "slow_operation"},
timeout=5.0 # Override timeout for this specific call
# Send an event with a custom timeout
success = logger.log_event(
properties={"event": "custom_event"},
timeout=1.0 # Override default timeout for this call
)
```

## API Reference

### ScarfEventLogger

#### `__init__(api_key: str, base_url: str = "https://api.scarf.sh/api/v1", timeout: Optional[float] = None)`

Initialize a new Scarf event logger.
# Empty properties are allowed
success = logger.log_event({})
```

- `api_key`: Your Scarf API key
- `base_url`: The base URL for the Scarf API (optional)
- `timeout`: Default timeout in seconds for API calls (optional, default: 3.0)
## Configuration

#### `log_event(properties: Dict[str, Any], timeout: Optional[float] = None) -> Optional[Dict[str, Any]]`
The client can be configured through environment variables:

Log a telemetry event to Scarf.
- `DO_NOT_TRACK=1`: Disable analytics
- `SCARF_NO_ANALYTICS=1`: Disable analytics (alternative)

- `properties`: Dictionary of properties to include with the event. All values must be simple types (str, int, float, bool, None).
- `timeout`: Optional timeout in seconds for this specific API call. Overrides the default timeout set in the constructor.
## Features

Returns the response from the Scarf API as a dictionary. Returns None if analytics are disabled via environment variables.
- Simple API for sending telemetry events
- Environment variable configuration
- Configurable timeouts (default: 3 seconds)
- Automatically reespects Do Not Track settings

### Environment Variables
## Development

Analytics can be disabled by setting either of these environment variables:
- `DO_NOT_TRACK=1` or `DO_NOT_TRACK=true`
- `SCARF_NO_ANALYTICS=1` or `SCARF_NO_ANALYTICS=true`
1. Clone the repository
2. Install development dependencies:
```bash
pip install -e ".[dev]"
```
3. Run tests:
```bash
pytest
```

## License

Apache 2.0
MIT
21 changes: 6 additions & 15 deletions examples/send_test_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,24 @@
Example script for sending test events to Scarf.
To run this example:
1. Set environment variables:
export SCARF_API_KEY="your-api-key"
export SCARF_BASE_URL="https://avi.gateway.scarf.sh/test-scarf-py" # Optional
2. Install the package in development mode:
1. Install the package in development mode:
pip install -e .
3. Run this script:
2. Run this script:
python examples/send_test_event.py
"""

import os

from requests.exceptions import RequestException

from scarf import ScarfEventLogger


def main():
try:
# Check for required environment variable
if not os.environ.get('SCARF_API_KEY'):
print("Error: SCARF_API_KEY environment variable is not set")
return 1

# Initialize the logger using environment variables
logger = ScarfEventLogger()
# Initialize the logger with a test endpoint
logger = ScarfEventLogger(
endpoint_url="https://avi.gateway.scarf.sh/test-scarf-py"
)

# Send a test event with two fields
success = logger.log_event({
Expand Down
25 changes: 8 additions & 17 deletions scarf/event_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,25 @@ class ScarfEventLogger:

def __init__(
self,
api_key: Optional[str] = None,
base_url: Optional[str] = None,
endpoint_url: str,
timeout: Optional[float] = None,
):
"""Initialize the Scarf event logger.
Args:
api_key: Your Scarf API key (optional, defaults to SCARF_API_KEY env var)
base_url: The base URL for the Scarf API (optional, defaults to SCARF_BASE_URL env var)
endpoint_url: The endpoint URL for the Scarf API
timeout: Default timeout in seconds for API calls (optional, default: 3.0)
Raises:
ValueError: If neither api_key parameter nor SCARF_API_KEY env var is set
ValueError: If endpoint_url is not provided or is empty
"""
self.api_key = api_key or os.environ.get('SCARF_API_KEY')
if not self.api_key:
raise ValueError(
"API key must be provided either through api_key parameter "
"or SCARF_API_KEY environment variable"
)

self.base_url = (
base_url or os.environ.get('SCARF_BASE_URL', "https://scarf.sh/api/v1")
).rstrip('/')
if not endpoint_url:
raise ValueError("endpoint_url must be provided")

self.endpoint_url = endpoint_url.rstrip('/')
self.timeout = timeout if timeout is not None else self.DEFAULT_TIMEOUT
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {self.api_key}',
'User-Agent': f'scarf-py/{__version__}'
})

Expand Down Expand Up @@ -105,7 +96,7 @@ def log_event(
self._validate_properties(properties)

response = self.session.post(
self.base_url,
self.endpoint_url,
params=properties,
timeout=timeout if timeout is not None else self.timeout
)
Expand Down
58 changes: 33 additions & 25 deletions tests/test_event_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@


class TestScarfEventLogger(unittest.TestCase):
DEFAULT_ENDPOINT = "https://scarf.sh/api/v1"

def setUp(self):
"""Reset environment variables before each test."""
# Store original environment variables
Expand All @@ -31,26 +33,32 @@ def tearDown(self):

def test_initialization(self):
"""Test that we can create a ScarfEventLogger instance."""
logger = ScarfEventLogger(api_key="test-api-key")
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT)
self.assertIsInstance(logger, ScarfEventLogger)
self.assertEqual(logger.api_key, "test-api-key")
self.assertEqual(logger.base_url, "https://scarf.sh/api/v1")
self.assertEqual(logger.endpoint_url, self.DEFAULT_ENDPOINT)
self.assertEqual(logger.timeout, ScarfEventLogger.DEFAULT_TIMEOUT)

def test_initialization_validation(self):
"""Test that initialization fails with invalid endpoint_url."""
with self.assertRaises(ValueError):
ScarfEventLogger(endpoint_url="")
with self.assertRaises(ValueError):
ScarfEventLogger(endpoint_url=None)

def test_custom_timeout(self):
"""Test that we can initialize with a custom timeout."""
logger = ScarfEventLogger(api_key="test-api-key", timeout=5.0)
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT, timeout=5.0)
self.assertEqual(logger.timeout, 5.0)

def test_custom_base_url(self):
"""Test that we can initialize with a custom base URL."""
def test_custom_endpoint_url(self):
"""Test that we can initialize with a custom endpoint URL."""
custom_url = "https://custom.scarf.sh/api/v1"
logger = ScarfEventLogger(api_key="test-api-key", base_url=custom_url)
self.assertEqual(logger.base_url, custom_url)
logger = ScarfEventLogger(endpoint_url=custom_url)
self.assertEqual(logger.endpoint_url, custom_url)

def test_validate_properties_simple_types(self):
"""Test that simple type properties are accepted."""
logger = ScarfEventLogger(api_key="test-api-key")
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT)

# These should not raise any errors
logger._validate_properties({
Expand All @@ -63,7 +71,7 @@ def test_validate_properties_simple_types(self):

def test_validate_properties_complex_types(self):
"""Test that complex type properties are rejected."""
logger = ScarfEventLogger(api_key="test-api-key")
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT)

invalid_properties = [
({'list': [1, 2, 3]}, "list"),
Expand All @@ -85,12 +93,12 @@ def test_empty_properties_allowed(self, mock_session):
mock_response.status_code = 200
mock_session.return_value.post.return_value = mock_response

logger = ScarfEventLogger(api_key="test-api-key")
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT)
result = logger.log_event({})

self.assertTrue(result)
mock_session.return_value.post.assert_called_with(
'https://scarf.sh/api/v1',
self.DEFAULT_ENDPOINT,
params={},
timeout=3.0
)
Expand All @@ -100,13 +108,13 @@ def test_request_timeout(self, mock_session):
"""Test that requests timeout after the specified duration."""
mock_session.return_value.post.side_effect = Timeout("Request timed out")

logger = ScarfEventLogger(api_key="test-api-key", timeout=1)
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT, timeout=1)

with self.assertRaises(Timeout):
logger.log_event({"event": "test"})

mock_session.return_value.post.assert_called_with(
'https://scarf.sh/api/v1',
self.DEFAULT_ENDPOINT,
params={"event": "test"},
timeout=1
)
Expand All @@ -118,12 +126,12 @@ def test_request_timeout_override(self, mock_session):
mock_response.status_code = 200
mock_session.return_value.post.return_value = mock_response

logger = ScarfEventLogger(api_key="test-api-key", timeout=3.0)
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT, timeout=3.0)
result = logger.log_event({"event": "test"}, timeout=1.0)

self.assertTrue(result)
mock_session.return_value.post.assert_called_with(
'https://scarf.sh/api/v1',
self.DEFAULT_ENDPOINT,
params={"event": "test"},
timeout=1.0
)
Expand Down Expand Up @@ -185,13 +193,13 @@ def test_do_not_track_env_var(self, mock_session):

for value, should_send in test_cases:
os.environ['DO_NOT_TRACK'] = value
logger = ScarfEventLogger(api_key="test-api-key")
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT)
result = logger.log_event(test_properties)

if should_send:
self.assertTrue(result)
mock_session.return_value.post.assert_called_with(
'https://scarf.sh/api/v1',
self.DEFAULT_ENDPOINT,
params=test_properties,
timeout=3.0
)
Expand Down Expand Up @@ -223,13 +231,13 @@ def test_scarf_no_analytics_env_var(self, mock_session):

for value, should_send in test_cases:
os.environ['SCARF_NO_ANALYTICS'] = value
logger = ScarfEventLogger(api_key="test-api-key")
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT)
result = logger.log_event(test_properties)

if should_send:
self.assertTrue(result)
mock_session.return_value.post.assert_called_with(
'https://scarf.sh/api/v1',
self.DEFAULT_ENDPOINT,
params=test_properties,
timeout=3.0
)
Expand All @@ -250,7 +258,7 @@ def test_env_var_precedence(self, mock_session):

os.environ['DO_NOT_TRACK'] = 'false'
os.environ['SCARF_NO_ANALYTICS'] = 'true'
logger = ScarfEventLogger(api_key="test-api-key")
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT)

result = logger.log_event({'event': 'test'})
self.assertFalse(result)
Expand All @@ -269,23 +277,23 @@ def mock_post(*args, **kwargs):
mock_session.return_value.post.side_effect = mock_post

# Should timeout (1s timeout, requires 2s)
logger = ScarfEventLogger(api_key="test-api-key", timeout=1)
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT, timeout=1)
with self.assertRaises(ReadTimeout):
logger.log_event({"event": "test"})

# Should succeed (3s timeout, requires 2s)
logger = ScarfEventLogger(api_key="test-api-key", timeout=3)
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT, timeout=3)
result = logger.log_event({"event": "test"})
self.assertTrue(result)

# Should timeout with per-request override (3s default, 1s override)
logger = ScarfEventLogger(api_key="test-api-key", timeout=3)
logger = ScarfEventLogger(endpoint_url=self.DEFAULT_ENDPOINT, timeout=3)
with self.assertRaises(ReadTimeout):
logger.log_event({"event": "test"}, timeout=1)

# Verify the last timeout value was passed correctly
mock_session.return_value.post.assert_called_with(
'https://scarf.sh/api/v1',
self.DEFAULT_ENDPOINT,
params={"event": "test"},
timeout=1
)
Expand Down

0 comments on commit a404807

Please sign in to comment.