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
24 changes: 23 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Bicep compiled files
*.json
infra/**/*.json
!infra/main.bicepparam

# Build artifacts
dist/
Expand All @@ -9,6 +10,27 @@ out/
# Dependency directories
node_modules/

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
.venv
pip-log.txt
pip-delete-this-directory.txt
.pytest_cache/
*.egg-info/

# Azure Functions
bin/
obj/
appsettings.json
local.settings.json

# Environment files
.env
.env.local
Expand Down
166 changes: 166 additions & 0 deletions IMPLEMENTATION_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Implementation Summary: Azure Function MCP Endpoints

## Overview
This implementation adds Python-based Azure Functions with MCP (Model Context Protocol) endpoints to the pi-chat project, enabling the application to send telemetry requests and action commands to Azure Service Bus topics.

## Changes Made

### 1. Infrastructure Updates

#### Service Bus (infra/modules/serviceBus.bicep)
- **Added two Service Bus topics**:
- `Telemetry` - for routing sensor telemetry data
- `Action` - for routing device action commands
- Both topics configured with:
- Max size: 1024 MB
- Message TTL: 14 days
- Batch operations: Enabled

#### Function App (infra/modules/functionApp.bicep)
- **Changed runtime from Node.js to Python**
- Removed `WEBSITE_NODE_DEFAULT_VERSION` setting
- Updated `FUNCTIONS_WORKER_RUNTIME` to `python`
- Maintained Service Bus connection configuration

#### Git Configuration (.gitignore)
- Updated to exclude Python-specific artifacts (`__pycache__`, `*.pyc`, etc.)
- Added Azure Functions specific exclusions (`bin/`, `obj/`, `local.settings.json`)
- Changed from excluding all `*.json` files to only excluding compiled Bicep files

### 2. Azure Functions Implementation

#### Project Structure
```
functions/
├── .funcignore # Files to exclude from deployment
├── GetTelemetry/
│ ├── __init__.py # GetTelemetry function implementation
│ └── function.json # Function binding configuration
├── SendAction/
│ ├── __init__.py # SendAction function implementation
│ └── function.json # Function binding configuration
├── host.json # Function app configuration
├── local.settings.json # Local development settings
├── requirements.txt # Python dependencies
├── test_endpoints.py # Python test script
├── test_endpoints.sh # Bash test script
└── README.md # Function documentation
```

#### GetTelemetry Endpoint
- **HTTP Method**: POST
- **Route**: `/api/GetTelemetry`
- **Request Body**:
```json
{
"SensorKey": "string",
"StartDate": "ISO 8601 datetime",
"EndDate": "ISO 8601 datetime"
}
```
- **Functionality**: Validates request and sends message to Service Bus "Telemetry" topic
- **Response**: JSON status message

#### SendAction Endpoint
- **HTTP Method**: POST
- **Route**: `/api/SendAction`
- **Request Body**:
```json
{
"ActionType": "string",
"ActionSpec": "string (raw JSON)"
}
```
- **Functionality**: Validates request and sends message to Service Bus "Action" topic
- **Response**: JSON status message

### 3. Testing Infrastructure

#### Test Scripts
- **test_endpoints.sh**: Bash script for automated endpoint testing
- **test_endpoints.py**: Python script for automated endpoint testing
- Both scripts test both endpoints and provide clear pass/fail results

### 4. Documentation Updates

#### functions/README.md (NEW)
- Comprehensive documentation for the Azure Functions
- Setup and deployment instructions
- API documentation with request/response examples
- Local development guide
- Testing instructions

#### infra/DEPLOYMENT.md
- Updated to reference Python runtime
- Added information about Service Bus topics
- Updated deployment instructions for Python functions
- Added links to function documentation

#### infra/README.md
- Updated architecture description to reflect Python runtime
- Added Service Bus topics to resource details
- Updated Function App description with endpoint information
- Added "Function Endpoints" section with API overview

## Key Features

1. **Python-based Azure Functions**: Modern, maintainable Python code
2. **Service Bus Topic Integration**: Messages sent to dedicated topics for routing
3. **Comprehensive Error Handling**: Validation and error responses
4. **Complete Documentation**: Setup, deployment, and testing guides
5. **Automated Testing**: Scripts for quick validation
6. **Infrastructure as Code**: All resources defined in Bicep

## Deployment Steps

1. Deploy infrastructure using Bicep templates:
```bash
az deployment group create \
--resource-group rg-pichat-dev \
--template-file infra/main.bicep \
--parameters infra/main.bicepparam
```

2. Deploy Python functions:
```bash
cd functions
func azure functionapp publish pichat-dev-func --python
```

3. Test endpoints:
```bash
./test_endpoints.sh https://pichat-dev-func.azurewebsites.net <function-key>
```

## Technical Details

### Dependencies
- **azure-functions**: Azure Functions runtime
- **azure-servicebus**: Service Bus SDK (>=7.11.0)

### Security
- Function endpoints use function-level authentication
- Service Bus connection string stored securely in app settings
- HTTPS enforced
- TLS 1.2 minimum

### Architecture Flow
```
HTTP Request → Azure Function → Service Bus Topic → [Downstream Consumers]
```

## Validation

All components have been validated:
- ✓ Bicep files compile without errors
- ✓ Python syntax is valid
- ✓ JSON configuration files are valid
- ✓ Git configuration properly excludes build artifacts

## Next Steps

1. Deploy infrastructure to Azure
2. Deploy function code
3. Test endpoints using provided scripts
4. Configure downstream Service Bus topic subscribers
5. Integrate with Raspberry Pi sensors and actuators
11 changes: 11 additions & 0 deletions functions/.funcignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.git*
.vscode
__azurite_db*__.json
__blobstorage__
__queuestorage__
local.settings.json
test
.python_packages
.venv
venv
env
74 changes: 74 additions & 0 deletions functions/GetTelemetry/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import json
import logging
import os
import azure.functions as func
from azure.servicebus import ServiceBusClient, ServiceBusMessage


def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('GetTelemetry function processing a request.')

try:
# Parse the request body
req_body = req.get_json()

# Validate required fields
if not req_body:
return func.HttpResponse(
"Please pass a TelemetryRequest in the request body",
status_code=400
)

sensor_key = req_body.get('SensorKey')
start_date = req_body.get('StartDate')
end_date = req_body.get('EndDate')

# Validate that all required fields are present
if not sensor_key or not start_date or not end_date:
return func.HttpResponse(
"TelemetryRequest must include SensorKey, StartDate, and EndDate",
status_code=400
)

# Create the telemetry request message
telemetry_request = {
'SensorKey': sensor_key,
'StartDate': start_date,
'EndDate': end_date
}

# Get Service Bus connection string from environment
connection_string = os.environ.get('ServiceBusConnectionString')

if not connection_string:
logging.error('ServiceBusConnectionString not configured')
return func.HttpResponse(
"Service Bus connection not configured",
status_code=500
)

# Send message to Service Bus topic
with ServiceBusClient.from_connection_string(connection_string) as client:
with client.get_topic_sender(topic_name="Telemetry") as sender:
message = ServiceBusMessage(json.dumps(telemetry_request))
sender.send_messages(message)
logging.info(f'Sent telemetry request to Service Bus topic: {telemetry_request}')

return func.HttpResponse(
json.dumps({"status": "success", "message": "Telemetry request sent"}),
mimetype="application/json",
status_code=200
)

except ValueError as e:
logging.error(f'Invalid JSON in request: {str(e)}')
return func.HttpResponse(
"Invalid JSON in request body",
status_code=400
)
except Exception as e:
logging.error(f'Error processing request: {str(e)}')
return func.HttpResponse(
f"Error processing request: {str(e)}",
status_code=500
)
19 changes: 19 additions & 0 deletions functions/GetTelemetry/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
Loading