re-uploading work
This commit is contained in:
598
docs/api/api-actions.md
Normal file
598
docs/api/api-actions.md
Normal file
@@ -0,0 +1,598 @@
|
||||
# Action Management API
|
||||
|
||||
This document describes the Action Management API endpoints for the Attune automation platform.
|
||||
|
||||
## Overview
|
||||
|
||||
Actions are the executable units in Attune that perform specific tasks. Each action belongs to a pack and can have parameters, output schemas, and runtime requirements.
|
||||
|
||||
**Base Path:** `/api/v1/actions`
|
||||
|
||||
## Data Model
|
||||
|
||||
### Action
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"ref": "core.http.get",
|
||||
"pack": 1,
|
||||
"pack_ref": "core",
|
||||
"label": "HTTP GET Request",
|
||||
"description": "Performs an HTTP GET request to a specified URL",
|
||||
"entrypoint": "/actions/http_get.py",
|
||||
"runtime": 1,
|
||||
"param_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": { "type": "string" },
|
||||
"headers": { "type": "object" }
|
||||
},
|
||||
"required": ["url"]
|
||||
},
|
||||
"out_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status_code": { "type": "integer" },
|
||||
"body": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"created": "2024-01-13T10:00:00Z",
|
||||
"updated": "2024-01-13T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Action Summary (List View)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"ref": "core.http.get",
|
||||
"pack_ref": "core",
|
||||
"label": "HTTP GET Request",
|
||||
"description": "Performs an HTTP GET request to a specified URL",
|
||||
"entrypoint": "/actions/http_get.py",
|
||||
"runtime": 1,
|
||||
"created": "2024-01-13T10:00:00Z",
|
||||
"updated": "2024-01-13T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### List All Actions
|
||||
|
||||
Retrieve a paginated list of all actions.
|
||||
|
||||
**Endpoint:** `GET /api/v1/actions`
|
||||
|
||||
**Query Parameters:**
|
||||
- `page` (integer, optional): Page number (default: 1)
|
||||
- `per_page` (integer, optional): Items per page (default: 20, max: 100)
|
||||
|
||||
**Response:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"ref": "core.http.get",
|
||||
"pack_ref": "core",
|
||||
"label": "HTTP GET Request",
|
||||
"description": "Performs an HTTP GET request",
|
||||
"entrypoint": "/actions/http_get.py",
|
||||
"runtime": 1,
|
||||
"created": "2024-01-13T10:00:00Z",
|
||||
"updated": "2024-01-13T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"per_page": 20,
|
||||
"total": 1,
|
||||
"total_pages": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### List Actions by Pack
|
||||
|
||||
Retrieve all actions belonging to a specific pack.
|
||||
|
||||
**Endpoint:** `GET /api/v1/packs/:pack_ref/actions`
|
||||
|
||||
**Path Parameters:**
|
||||
- `pack_ref` (string): Pack reference identifier
|
||||
|
||||
**Query Parameters:**
|
||||
- `page` (integer, optional): Page number (default: 1)
|
||||
- `per_page` (integer, optional): Items per page (default: 20, max: 100)
|
||||
|
||||
**Response:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"ref": "core.http.get",
|
||||
"pack_ref": "core",
|
||||
"label": "HTTP GET Request",
|
||||
"description": "Performs an HTTP GET request",
|
||||
"entrypoint": "/actions/http_get.py",
|
||||
"runtime": 1,
|
||||
"created": "2024-01-13T10:00:00Z",
|
||||
"updated": "2024-01-13T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"per_page": 20,
|
||||
"total": 1,
|
||||
"total_pages": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `404 Not Found`: Pack with the specified ref does not exist
|
||||
|
||||
---
|
||||
|
||||
### Get Action by Reference
|
||||
|
||||
Retrieve a single action by its reference identifier.
|
||||
|
||||
**Endpoint:** `GET /api/v1/actions/:ref`
|
||||
|
||||
**Path Parameters:**
|
||||
- `ref` (string): Action reference identifier (e.g., "core.http.get")
|
||||
|
||||
**Response:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"ref": "core.http.get",
|
||||
"pack": 1,
|
||||
"pack_ref": "core",
|
||||
"label": "HTTP GET Request",
|
||||
"description": "Performs an HTTP GET request to a specified URL",
|
||||
"entrypoint": "/actions/http_get.py",
|
||||
"runtime": 1,
|
||||
"param_schema": { ... },
|
||||
"out_schema": { ... },
|
||||
"created": "2024-01-13T10:00:00Z",
|
||||
"updated": "2024-01-13T10:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `404 Not Found`: Action with the specified ref does not exist
|
||||
|
||||
---
|
||||
|
||||
### Create Action
|
||||
|
||||
Create a new action in the system.
|
||||
|
||||
**Endpoint:** `POST /api/v1/actions`
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"ref": "core.http.get",
|
||||
"pack_ref": "core",
|
||||
"label": "HTTP GET Request",
|
||||
"description": "Performs an HTTP GET request to a specified URL",
|
||||
"entrypoint": "/actions/http_get.py",
|
||||
"runtime": 1,
|
||||
"param_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": { "type": "string" },
|
||||
"headers": { "type": "object" }
|
||||
},
|
||||
"required": ["url"]
|
||||
},
|
||||
"out_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status_code": { "type": "integer" },
|
||||
"body": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Required Fields:**
|
||||
- `ref`: Unique reference identifier (alphanumeric, dots, underscores, hyphens)
|
||||
- `pack_ref`: Reference to the parent pack
|
||||
- `label`: Human-readable name (1-255 characters)
|
||||
- `description`: Action description (min 1 character)
|
||||
- `entrypoint`: Execution entry point (1-1024 characters)
|
||||
|
||||
**Optional Fields:**
|
||||
- `runtime`: Runtime ID for execution environment
|
||||
- `param_schema`: JSON Schema defining input parameters
|
||||
- `out_schema`: JSON Schema defining expected outputs
|
||||
|
||||
**Response:** `201 Created`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"ref": "core.http.get",
|
||||
"pack": 1,
|
||||
"pack_ref": "core",
|
||||
"label": "HTTP GET Request",
|
||||
"description": "Performs an HTTP GET request to a specified URL",
|
||||
"entrypoint": "/actions/http_get.py",
|
||||
"runtime": 1,
|
||||
"param_schema": { ... },
|
||||
"out_schema": { ... },
|
||||
"created": "2024-01-13T10:00:00Z",
|
||||
"updated": "2024-01-13T10:00:00Z"
|
||||
},
|
||||
"message": "Action created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `400 Bad Request`: Invalid request data or validation failure
|
||||
- `404 Not Found`: Referenced pack does not exist
|
||||
- `409 Conflict`: Action with the same ref already exists
|
||||
|
||||
---
|
||||
|
||||
### Update Action
|
||||
|
||||
Update an existing action's properties.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/actions/:ref`
|
||||
|
||||
**Path Parameters:**
|
||||
- `ref` (string): Action reference identifier
|
||||
|
||||
**Request Body:**
|
||||
|
||||
All fields are optional. Only provided fields will be updated.
|
||||
|
||||
```json
|
||||
{
|
||||
"label": "Updated HTTP GET Request",
|
||||
"description": "Updated description",
|
||||
"entrypoint": "/actions/http_get_v2.py",
|
||||
"runtime": 2,
|
||||
"param_schema": { ... },
|
||||
"out_schema": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"ref": "core.http.get",
|
||||
"pack": 1,
|
||||
"pack_ref": "core",
|
||||
"label": "Updated HTTP GET Request",
|
||||
"description": "Updated description",
|
||||
"entrypoint": "/actions/http_get_v2.py",
|
||||
"runtime": 2,
|
||||
"param_schema": { ... },
|
||||
"out_schema": { ... },
|
||||
"created": "2024-01-13T10:00:00Z",
|
||||
"updated": "2024-01-13T12:00:00Z"
|
||||
},
|
||||
"message": "Action updated successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `400 Bad Request`: Invalid request data or validation failure
|
||||
- `404 Not Found`: Action with the specified ref does not exist
|
||||
|
||||
---
|
||||
|
||||
### Delete Action
|
||||
|
||||
Delete an action from the system.
|
||||
|
||||
**Endpoint:** `DELETE /api/v1/actions/:ref`
|
||||
|
||||
**Path Parameters:**
|
||||
- `ref` (string): Action reference identifier
|
||||
|
||||
**Response:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Action 'core.http.get' deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `404 Not Found`: Action with the specified ref does not exist
|
||||
|
||||
---
|
||||
|
||||
### Get Queue Statistics
|
||||
|
||||
Retrieve real-time queue statistics for an action's execution queue.
|
||||
|
||||
**Endpoint:** `GET /api/v1/actions/:ref/queue-stats`
|
||||
|
||||
**Path Parameters:**
|
||||
- `ref` (string): Action reference identifier
|
||||
|
||||
**Response:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action_id": 1,
|
||||
"action_ref": "core.http.get",
|
||||
"queue_length": 5,
|
||||
"active_count": 2,
|
||||
"max_concurrent": 3,
|
||||
"oldest_enqueued_at": "2025-01-27T10:30:00Z",
|
||||
"total_enqueued": 1250,
|
||||
"total_completed": 1245,
|
||||
"last_updated": "2025-01-27T12:45:30Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response Fields:**
|
||||
- `action_id`: Numeric action ID
|
||||
- `action_ref`: Action reference identifier
|
||||
- `queue_length`: Number of executions waiting in queue
|
||||
- `active_count`: Number of currently running executions
|
||||
- `max_concurrent`: Maximum concurrent executions allowed (from policy)
|
||||
- `oldest_enqueued_at`: Timestamp of oldest queued execution (null if queue empty)
|
||||
- `total_enqueued`: Lifetime count of executions enqueued
|
||||
- `total_completed`: Lifetime count of executions completed
|
||||
- `last_updated`: Last time statistics were updated
|
||||
|
||||
**Response When No Queue Stats Available:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action_id": 1,
|
||||
"action_ref": "core.http.get",
|
||||
"queue_length": 0,
|
||||
"active_count": 0,
|
||||
"max_concurrent": null,
|
||||
"oldest_enqueued_at": null,
|
||||
"total_enqueued": 0,
|
||||
"total_completed": 0,
|
||||
"last_updated": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `404 Not Found`: Action with the specified ref does not exist
|
||||
|
||||
**Use Cases:**
|
||||
- Monitor action execution queue depth
|
||||
- Detect stuck or growing queues
|
||||
- Track execution throughput
|
||||
- Validate policy enforcement
|
||||
- Operational dashboards
|
||||
|
||||
**Related Documentation:**
|
||||
- [Queue Architecture](./queue-architecture.md)
|
||||
- [Policy Enforcement](./executor-service.md#policy-enforcement)
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Creating a Simple Action
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/v1/actions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"ref": "mypack.hello_world",
|
||||
"pack_ref": "mypack",
|
||||
"label": "Hello World",
|
||||
"description": "Prints hello world",
|
||||
"entrypoint": "/actions/hello.py"
|
||||
}'
|
||||
```
|
||||
|
||||
### Creating an Action with Parameter Schema
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/v1/actions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"ref": "mypack.send_email",
|
||||
"pack_ref": "mypack",
|
||||
"label": "Send Email",
|
||||
"description": "Sends an email message",
|
||||
"entrypoint": "/actions/send_email.py",
|
||||
"param_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"to": { "type": "string", "format": "email" },
|
||||
"subject": { "type": "string" },
|
||||
"body": { "type": "string" }
|
||||
},
|
||||
"required": ["to", "subject", "body"]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Listing Actions for a Pack
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/api/v1/packs/core/actions
|
||||
```
|
||||
|
||||
### Updating an Action
|
||||
|
||||
```bash
|
||||
curl -X PUT http://localhost:3000/api/v1/actions/mypack.hello_world \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"label": "Hello World v2",
|
||||
"description": "Updated hello world action"
|
||||
}'
|
||||
```
|
||||
|
||||
### Deleting an Action
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://localhost:3000/api/v1/actions/mypack.hello_world
|
||||
```
|
||||
|
||||
### Getting Queue Statistics
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/api/v1/actions/core.http.get/queue-stats
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action_id": 1,
|
||||
"action_ref": "core.http.get",
|
||||
"queue_length": 12,
|
||||
"active_count": 5,
|
||||
"max_concurrent": 5,
|
||||
"oldest_enqueued_at": "2025-01-27T12:40:00Z",
|
||||
"total_enqueued": 523,
|
||||
"total_completed": 511,
|
||||
"last_updated": "2025-01-27T12:45:30Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation Rules
|
||||
|
||||
### Action Reference (`ref`)
|
||||
- Must be unique across all actions
|
||||
- Can contain alphanumeric characters, dots (.), underscores (_), and hyphens (-)
|
||||
- Typically follows the pattern: `pack_name.action_name`
|
||||
- Example: `core.http.get`, `aws.ec2.start_instance`
|
||||
|
||||
### Pack Reference (`pack_ref`)
|
||||
- Must reference an existing pack
|
||||
- The pack must exist before creating actions for it
|
||||
|
||||
### Entry Point (`entrypoint`)
|
||||
- Path or identifier for the executable code
|
||||
- Can be a file path, module name, function name, etc.
|
||||
- Format depends on the runtime environment
|
||||
|
||||
### Schemas (`param_schema`, `out_schema`)
|
||||
- Must be valid JSON Schema documents
|
||||
- Used for validation during action execution
|
||||
- Helps with auto-generating documentation and UI
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Naming Conventions**
|
||||
- Use descriptive, hierarchical names: `pack.category.action`
|
||||
- Keep names concise but meaningful
|
||||
- Use lowercase with dots as separators
|
||||
|
||||
2. **Schema Definitions**
|
||||
- Always provide `param_schema` for clarity
|
||||
- Define `required` fields in schemas
|
||||
- Use appropriate JSON Schema types and formats
|
||||
- Document schema fields with descriptions
|
||||
|
||||
3. **Entry Points**
|
||||
- Use consistent paths relative to the pack root
|
||||
- Keep entry points simple and maintainable
|
||||
- Consider versioning: `/actions/v1/http_get.py`
|
||||
|
||||
4. **Runtime Association**
|
||||
- Specify runtime when actions have specific dependencies
|
||||
- Null runtime means use default/generic runtime
|
||||
- Ensure runtime exists before creating action
|
||||
|
||||
5. **Error Handling**
|
||||
- Design actions to handle errors gracefully
|
||||
- Use output schemas to define error structures
|
||||
- Log execution details for debugging
|
||||
|
||||
6. **Queue Monitoring**
|
||||
- Use `/queue-stats` endpoint to monitor execution queues
|
||||
- Alert on high `queue_length` (> 100)
|
||||
- Investigate when `oldest_enqueued_at` is old (> 30 minutes)
|
||||
- Track completion rate: `total_completed / total_enqueued`
|
||||
|
||||
---
|
||||
|
||||
## Queue Statistics
|
||||
|
||||
The `/queue-stats` endpoint provides real-time visibility into action execution queues.
|
||||
|
||||
### Understanding Queue Metrics
|
||||
|
||||
- **queue_length**: Executions waiting to run (0 = healthy)
|
||||
- **active_count**: Executions currently running
|
||||
- **max_concurrent**: Policy-enforced concurrency limit
|
||||
- **oldest_enqueued_at**: How long the oldest execution has been waiting
|
||||
- **total_enqueued/completed**: Lifetime throughput metrics
|
||||
|
||||
### Healthy vs Unhealthy Queues
|
||||
|
||||
**Healthy:**
|
||||
- ✅ `queue_length` is 0 or low (< 10)
|
||||
- ✅ `active_count` ≈ `max_concurrent` during load
|
||||
- ✅ `oldest_enqueued_at` is recent (< 5 minutes)
|
||||
- ✅ `total_completed` increases steadily
|
||||
|
||||
**Unhealthy:**
|
||||
- ⚠️ `queue_length` consistently high (> 50)
|
||||
- ⚠️ `oldest_enqueued_at` is old (> 30 minutes)
|
||||
- 🚨 Queue not progressing (stats not updating)
|
||||
- 🚨 `active_count` < `max_concurrent` (workers stuck)
|
||||
|
||||
### Monitoring Recommendations
|
||||
|
||||
1. **Set up alerts** for high queue depths
|
||||
2. **Track trends** in `total_enqueued` vs `total_completed`
|
||||
3. **Investigate spikes** in `queue_length`
|
||||
4. **Scale workers** when queues consistently fill
|
||||
5. **Adjust policies** if concurrency limits are too restrictive
|
||||
|
||||
For detailed queue architecture and troubleshooting, see [Queue Architecture Documentation](./queue-architecture.md).
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Pack Management API](./api-packs.md)
|
||||
- [Runtime Management API](./api-runtimes.md)
|
||||
- [Rule Management API](./api-rules.md)
|
||||
- [Execution API](./api-executions.md)
|
||||
- [Queue Architecture](./queue-architecture.md)
|
||||
- [Executor Service](./executor-service.md)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** January 27, 2025
|
||||
Reference in New Issue
Block a user