# OWS Python Service Patterns

## Flask Application Structure

All OWS services follow:
```
application.py  → Entry point (patches ddtrace, imports app)
api.py          → Flask app creation, SQLAlchemy setup
handlers.py     → Route definitions
config.py       → Environment configuration
```

## Snowflake Connection

- **Authentication**: Private key pair (PKI) — never password-based
- **Pool**: QueuePool (15 connections, 4hr recycle)
- **Dev**: Private key from `~/.ssh/snowflake/`
- **QA/Prod**: Private key from AWS Secrets Manager

## Request Authorization

All requests authenticated via `owsrequest` library:
- Grass headers: account type, account ID, profile ID
- DynamoDB-backed permissions
- Optional YAML access rules (`access_rules.yml` in ows-analytics)

## Response Format

Consistent JSON envelope via `oto` library or Marshmallow schemas:
```json
{
    "success": true,
    "data": { ... },
    "error": null
}
```

## Caching

Redis with TTL decorators. FakeRedis fallback for local development:
```python
@cache_in_redis(ttl=3600)
def get_data(params):
    ...
```

## Testing

- autouse fixtures mock Snowflake, Redis, features
- Markers: `disable_mock_cache`, `disable_mock_execute`
- Coverage target: 80%+
- Unit tests never hit real Snowflake

## Linting

- **Black**: 88 char line length
- **isort**: Black-compatible profile
- **flake8**: Complexity 18, ignore E203/E266/E501/W503
- Pre-commit hooks enforce all three

## Feature Flags

Via `pythonfeatures` library:
```python
from features import check_feature_flag

if check_feature_flag('my_flag', context):
    ...
```

## Observability

- **APM**: Datadog (`ddtrace`) — patched at application entry
- **Errors**: Sentry (`raven` or `sentry-sdk`)
- **Logging**: `owslogger`
