niteco.opal-rest-tool


Nameniteco.opal-rest-tool JSON
Version 0.1.4 PyPI version JSON
download
home_pageNone
SummaryOPAL REST Tool Service - Automatically generates tools from OpenAPI 3.x specifications
upload_time2025-08-14 12:12:58
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords openapi rest api tools opal fastapi
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # OPAL REST Tool Service

A robust implementation of REST API tools for the OPAL SDK that automatically generates tools from OpenAPI specifications.

## Overview

The RESTToolService extends the standard OPAL ToolsService to support automatic tool generation from OpenAPI 3.x specifications. Instead of manually writing wrapper functions for each API endpoint, you can simply provide an OpenAPI spec and get all operations as callable tools.

## Features

- ✅ **Zero-code tool generation** - REST APIs become tools automatically
- ✅ **OpenAPI 3.x support** - Parse JSON or YAML specifications  
- ✅ **Flexible authentication** - Support for API keys, OAuth2, Bearer tokens
- ✅ **Parameter mapping** - Convert OpenAPI parameters to OPAL Parameter types
- ✅ **HTTP client integration** - Built-in async HTTP client for API calls
- ✅ **Error handling** - Proper status code and error response handling
- ✅ **Backward compatibility** - Works alongside traditional @tool decorators
- ✅ **Configuration options** - Filter operations, custom base URLs, auth config

## REST Tool Flow

The OPAL REST Tool Service follows a clear pipeline that transforms OpenAPI specifications into executable tools:

### 1. Data Flow Architecture

```
┌─────────────────┐    ┌────────────────────┐    ┌─────────────────┐
│   OpenAPI       │───▶│  OpenAPISpecParser │───▶│ ParsedOperation │
│   Specification │    │                    │    │     List        │
│   (JSON/YAML)   │    │                    │    │                 │
└─────────────────┘    └────────────────────┘    └─────────────────┘
                                                            │
                                                            ▼
┌─────────────────┐    ┌────────────────────┐    ┌─────────────────┐
│   HTTP Client   │◀───│   RESTHandler      │◀───│ RESTToolService │
│   (httpx)       │    │                    │    │                 │
└─────────────────┘    └────────────────────┘    └─────────────────┘
                                                            │
                                                            ▼
┌─────────────────┐    ┌────────────────────┐    ┌─────────────────┐
│   FastAPI       │◀───│ RESTToolRegistry   │◀───│   Tool Handler  │
│   Endpoints     │    │                    │    │   Functions     │
└─────────────────┘    └────────────────────┘    └─────────────────┘
```

### 2. Processing Steps

1. **OpenAPI Parsing**: `OpenAPISpecParser` reads the specification and extracts operations
2. **Operation Registration**: Each operation becomes a `ParsedOperation` with parameters and metadata
3. **Tool Creation**: Operations are registered as FastAPI endpoints through `RESTToolService`
4. **Registry Management**: `RESTToolRegistry` tracks all operations with filtering and statistics
5. **Request Execution**: `RESTHandler` executes HTTP calls to target APIs
6. **Response Handling**: Results are formatted and returned to the client

### 3. Component Interaction

```
Request → FastAPI Endpoint → Tool Handler → RESTHandler → Target API
                                                 ↓
Response ← Tool Handler ← RESTHandler ← Target API Response
```

## Installation

1. Install required dependencies:
```bash
pip install -r requirements.txt
```

2. Configure environment variables (optional):
```bash
# Copy the example environment file
cp .env.example .env

# Edit .env with your API keys and configuration
# BEARER_AUTHENTICATION=your-api-key-here
```

## Quick Start

Get started with REST tools in 3 simple steps:

### 1. Run the Example
```bash
# Run the Optimizely API example
python -m rest_tools_example
```

## How to Add New Tools

There are multiple ways to add new REST tools to the OPAL system, depending on your use case:

### Method 1: From OpenAPI Specification URL

The easiest way to add tools from a publicly accessible OpenAPI spec:

```python
from fastapi import FastAPI
from core.rest_tool_service import create_service_from_urls
from core.rest_models import OpenAPIConfig

app = FastAPI(title="My REST Tools Service")

# Configure which operations to include
config = OpenAPIConfig(
    base_url="https://api.example.com/v1",  # Override spec base URL if needed
    filter_operations=["list_users", "get_user"],  # Only include these operations
    # exclude_operations=["delete_all"]  # Or exclude specific operations
)

# Create service from URL
service = await create_service_from_urls(
    app, 
    {"my-api": "https://api.example.com/openapi.json"},  # spec_name: url
    config
)

await service.startup()
```

### Method 2: From Local OpenAPI Specification File

When you have the OpenAPI spec as a local file:

```python
from pathlib import Path
from core.rest_tool_service import create_rest_tool_service

# Load from file path
spec_path = Path("my-api-spec.json")  # or .yaml
service = create_rest_tool_service(app, spec_path, config)

# Or load and filter the spec first
import json
with open("full-api-spec.json") as f:
    full_spec = json.load(f)

# Filter to specific paths
filtered_spec = {
    **full_spec,
    "paths": {path: methods for path, methods in full_spec["paths"].items() 
              if path.startswith("/users")}  # Only /users endpoints
}

service = create_rest_tool_service(app, filtered_spec, config)
```

### Method 3: Multiple API Specifications

When you need to combine multiple APIs into one service:

```python
from core.rest_tool_service import create_multi_spec_service

# Multiple specs with names
specs = {
    "github-api": github_openapi_spec,
    "slack-api": slack_openapi_spec,
    "custom-api": Path("custom-api.yaml")
}

service = create_multi_spec_service(app, specs, config)
await service.startup()

# Access operations by spec
github_ops = service.get_operations(spec_name="github-api")
slack_ops = service.get_operations(spec_name="slack-api")
```

### Method 4: Dynamic Registration

Add specs dynamically to a running service:

```python
# Start with empty service
service = RESTToolService(app, config)
await service.startup()

# Add specs as needed
count1 = service.register_openapi_spec(api_spec_1, "api-v1")
count2 = await service.register_openapi_url("https://api.example.com/spec", "api-v2")

# Bulk registration
batch_specs = [
    {"spec": spec_1, "name": "spec1"},
    {"url": "https://api.example.com/spec2", "name": "spec2"}
]
results = service.register_multiple_specs(batch_specs)
```

### Configuration Options

Control which operations become tools using `OpenAPIConfig`:

```python
config = OpenAPIConfig(
    base_url="https://custom-base.com",           # Override API base URL
    filter_operations=["op1", "op2"],            # Only include these operations
    exclude_operations=["dangerous_op"],          # Exclude specific operations
    endpoint_prefix="/custom/api/path",           # Custom endpoint prefix
)
```

### Authentication Setup

Configure authentication for API calls:

```python
# In your tool calls, provide auth data:
auth_data = {
    # Direct headers
    "Authorization": "Bearer your-token",
    "X-API-Key": "your-api-key",
    
    # Or structured auth (matches OpenAPI security schemes)
    "api_key": "your-key",
    "token": "bearer-token",
    "access_token": "oauth-token"
}

# The RESTHandler will automatically apply the appropriate headers
result = await service.rest_handler.execute_operation(
    operation=my_operation,
    parameters={"param1": "value1"},
    auth_data=auth_data
)
```

#### Environment Variables

The system supports configuration via environment variables:

```bash
# Set in your .env file or environment

# Authentication
BEARER_AUTHENTICATION=your-api-key-here

# REST API Configuration
RESTAPI_ENDPOINT_PREFIX=/rest-tools  # Default endpoint prefix for tools
```

Configuration details:
- `BEARER_AUTHENTICATION`: API authentication token (loaded by `get_auth_value()`)
- `RESTAPI_ENDPOINT_PREFIX`: Configurable endpoint prefix for REST tools (default: `/rest-tools`)

#### Configurable Endpoint Prefix

You can customize the endpoint prefix for REST tools using the `RESTAPI_ENDPOINT_PREFIX` environment variable:

```bash
# Examples:
RESTAPI_ENDPOINT_PREFIX=/rest-tools           # Default
RESTAPI_ENDPOINT_PREFIX=/experimental/rest-tools
RESTAPI_ENDPOINT_PREFIX=/api/v2/tools
```

This affects all tool endpoints. For example, with `/experimental/rest-tools`:
- `list_projects` becomes `/experimental/rest-tools/list_projects`
- `get_user` becomes `/experimental/rest-tools/get_user`

You can also override in code:

```python
config = OpenAPIConfig(
    endpoint_prefix="/custom/api/path"  # Overrides environment variable
)
```

## Core Components

The system consists of several key components that work together to transform OpenAPI specs into executable tools:

### 1. OpenAPISpecParser (`niteco/opal_rest_tool/openapi_parser.py:12`)
Parses OpenAPI 3.x specifications and extracts operations into `ParsedOperation` objects:

**Key Features:**
- Supports JSON and YAML OpenAPI specs
- Extracts parameters from paths, query strings, and request bodies
- Maps OpenAPI security schemes to authentication requirements, can omit this (for Opal Hackathon)
- Applies operation filtering based on configuration

```python
from core.openapi_parser import OpenAPISpecParser
from core.rest_models import OpenAPIConfig

config = OpenAPIConfig(filter_operations=["list_users", "get_user"])
parser = OpenAPISpecParser(config)
operations = parser.parse(openapi_spec)  # Returns List[ParsedOperation]
```

### 2. RESTHandler (`niteco/opal_rest_tool/rest_handler.py:13`)
Executes HTTP requests for parsed operations using the httpx client:

**Key Features:**
- Builds URLs with path parameter substitution
- Handles bearer authentication
- Separates parameters into query params and request body
- Formats responses with error handling

```python
from core.rest_handler import RESTHandler

handler = RESTHandler(timeout=30)
result = await handler.execute_operation(
    operation=parsed_operation,
    parameters={"user_id": 123, "include_details": True},
    auth_data={"Authorization": "Bearer token"}
)
```

### 3. ParsedOperation (`niteco/opal_rest_tool/rest_models.py:10`)
Data model representing a parsed API operation with all necessary metadata:

```python
@dataclass
class ParsedOperation:
    name: str                                    # Generated tool name (snake_case)
    description: str                             # Operation description
    method: str                                  # HTTP method (GET, POST, etc.)
    path: str                                    # API path with parameters
    base_url: str                                # Base URL for API calls
    parameters: List[Parameter]                  # Extracted parameters
    auth_requirements: Optional[List[AuthRequirement]]  # Auth requirements
    operation_id: Optional[str]                  # Original OpenAPI operationId
    request_body_schema: Optional[Dict[str, Any]]  # Request body schema
    response_schema: Optional[Dict[str, Any]]    # Response schema
    tags: Optional[List[str]]                    # OpenAPI tags
```

### 4. RESTToolService (`niteco/opal_rest_tool/rest_tool_service.py:117`)
Main service class that extends the OPAL ToolsService to support REST API integration:

**Key Features:**
- Auto-registration of OpenAPI operations as FastAPI endpoints
- Operation registry with filtering and management
- Health monitoring and validation
- Multi-spec support with bulk operations
- Lifecycle management (startup/shutdown)

```python
from core.rest_tool_service import RESTToolService
from core.rest_models import OpenAPIConfig

app = FastAPI()
config = OpenAPIConfig(base_url="https://api.example.com")
service = RESTToolService(app, config)

# Register specs
count = service.register_openapi_spec(openapi_spec, "my-api")
count = await service.register_openapi_url("https://api.example.com/spec", "api-v2")

# Manage operations
operations = service.get_operations(spec_name="my-api")
service.unregister_operation("unwanted_tool")
```

### 5. RESTToolRegistry (`niteco/opal_rest_tool/rest_tool_service.py:26`)
Internal registry that manages REST operations with metadata tracking:

**Key Features:**
- Operation storage with name-based lookup
- Tag-based and spec-based filtering
- Statistics and health monitoring
- Bulk operations (clear, unregister)

```python
# Accessed through RESTToolService
stats = service.registry.get_stats()
operations_by_tag = service.registry.get_by_tag("users")
operations_by_spec = service.registry.get_by_spec("github-api")
```

### 6. OpenAPIConfig (`niteco/opal_rest_tool/rest_models.py:38`)
Configuration object for customizing OpenAPI parsing and tool generation:

```python
@dataclass
class OpenAPIConfig:
    base_url: Optional[str] = None                # Override API base URL
    auth_config: Optional[Dict[str, Any]] = None  # Auth configuration
    filter_operations: Optional[List[str]] = None # Include only these operations
    exclude_operations: Optional[List[str]] = None # Exclude these operations
```

## Testing

Run the test suite to see the RESTToolService in action:

```bash
# Test the parser only
python test_rest_service.py
# Choose option 1

# Test the full service with HTTP calls
python test_rest_service.py  
# Choose option 2
```

The test uses the JSONPlaceholder API (https://jsonplaceholder.typicode.com) to demonstrate:
- GET requests with query parameters
- POST requests with request body
- Path parameter substitution
- Response handling

## Authentication Support

The RESTToolService supports bearer authentication method for now.

## Error Handling

- HTTP errors (4xx, 5xx) are captured and included in responses
- Network errors are logged and re-raised as HTTPException
- Invalid OpenAPI specs raise ValueError with details
- Missing required parameters are logged as warnings

## Discovery Endpoint

All generated tools are automatically discoverable via the `/discovery` endpoint:

```json
{
  "functions": [
    {
      "name": "get_users",
      "description": "Get all users",
      "parameters": [...],
      "endpoint": "/rest-tools/get_users",
      "http_method": "POST"
    }
  ]
}
```

## Limitations

- Currently supports OpenAPI 3.x (not 2.x/Swagger)
- Reference resolution is simplified (no external refs)
- Authentication mapping may need customization for complex schemes
- Response schemas are captured but not validated

## Future Enhancements

- Full $ref reference resolution
- OpenAPI 2.x support
- Response validation against schemas
- Request/response transformation middleware
- Rate limiting and retry logic
- Caching for API responses

## Project Structure

### PyPI Package Structure (niteco.opal-rest-tool)
```
niteco/opal_rest_tool/                      # Core system components (included in PyPI)
├── __init__.py                             # Package initialization
├── rest_models.py                          # Data models (ParsedOperation, OpenAPIConfig)
├── openapi_parser.py                       # OpenAPI 3.x specification parser
├── rest_handler.py                         # HTTP client for REST API execution
└── rest_tool_service.py                    # Main service (RESTToolService, Registry)
```

### Development Repository Structure
```
Niteco.Opal-REST-tool/
├── niteco/opal_rest_tool/                  # Core system components (PyPI package)
│   ├── __init__.py                         # Package initialization
│   ├── rest_models.py                      # Data models (ParsedOperation, OpenAPIConfig)
│   ├── openapi_parser.py                   # OpenAPI 3.x specification parser
│   ├── rest_handler.py                     # HTTP client for REST API execution
│   └── rest_tool_service.py               # Main service (RESTToolService, Registry)
├── rest_tools_example/                     # Example implementations (dev only)
│   ├── __init__.py                         # Package initialization
│   ├── __main__.py                         # Example entry point
│   ├── optimizely_web_experimentation.py   # Optimizely API integration example
│   └── opal tool.postman_collection.json  # Postman collection for testing
├── test/                                   # Test files and specifications (dev only)
│   ├── Optimizely API - v1.0.json         # Sample OpenAPI specification
│   └── test_rest.py                        # Comprehensive test suite
├── .env.example                            # Environment configuration template
├── pyproject.toml                          # Package configuration and PyPI metadata
├── requirements.txt                        # Python dependencies
└── README.md                              # This documentation
```

### Core Files Description

- **`niteco/opal_rest_tool/rest_models.py`** - Data classes including `ParsedOperation` (line 10) and `OpenAPIConfig` (line 38)
- **`niteco/opal_rest_tool/openapi_parser.py`** - OpenAPI spec parser with `OpenAPISpecParser` class (line 12) 
- **`niteco/opal_rest_tool/rest_handler.py`** - HTTP execution engine with `RESTHandler` class (line 13)
- **`niteco/opal_rest_tool/rest_tool_service.py`** - Main service classes:
  - `RESTToolRegistry` (line 26) - Operation registry management
  - `RESTToolService` (line 117) - Main service extending OPAL ToolsService
  - Factory functions (lines 503-571) - Convenience service creators
- **`test/test_rest.py`** - Complete test suite demonstrating all features
- **`rest_tools_example/optimizely_web_experimentation.py`** - Real-world example using Optimizely API

## License

This implementation is part of the OPAL project.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "niteco.opal-rest-tool",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "openapi, rest, api, tools, opal, fastapi",
    "author": null,
    "author_email": "Niteco <info@niteco.com>",
    "download_url": "https://files.pythonhosted.org/packages/97/cb/326b12e96df6a873d416c75ab6a0929de4d6a9a3c1db8d488c93d88cac3a/niteco_opal_rest_tool-0.1.4.tar.gz",
    "platform": null,
    "description": "# OPAL REST Tool Service\r\n\r\nA robust implementation of REST API tools for the OPAL SDK that automatically generates tools from OpenAPI specifications.\r\n\r\n## Overview\r\n\r\nThe RESTToolService extends the standard OPAL ToolsService to support automatic tool generation from OpenAPI 3.x specifications. Instead of manually writing wrapper functions for each API endpoint, you can simply provide an OpenAPI spec and get all operations as callable tools.\r\n\r\n## Features\r\n\r\n- \u2705 **Zero-code tool generation** - REST APIs become tools automatically\r\n- \u2705 **OpenAPI 3.x support** - Parse JSON or YAML specifications  \r\n- \u2705 **Flexible authentication** - Support for API keys, OAuth2, Bearer tokens\r\n- \u2705 **Parameter mapping** - Convert OpenAPI parameters to OPAL Parameter types\r\n- \u2705 **HTTP client integration** - Built-in async HTTP client for API calls\r\n- \u2705 **Error handling** - Proper status code and error response handling\r\n- \u2705 **Backward compatibility** - Works alongside traditional @tool decorators\r\n- \u2705 **Configuration options** - Filter operations, custom base URLs, auth config\r\n\r\n## REST Tool Flow\r\n\r\nThe OPAL REST Tool Service follows a clear pipeline that transforms OpenAPI specifications into executable tools:\r\n\r\n### 1. Data Flow Architecture\r\n\r\n```\r\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n\u2502   OpenAPI       \u2502\u2500\u2500\u2500\u25b6\u2502  OpenAPISpecParser \u2502\u2500\u2500\u2500\u25b6\u2502 ParsedOperation \u2502\r\n\u2502   Specification \u2502    \u2502                    \u2502    \u2502     List        \u2502\r\n\u2502   (JSON/YAML)   \u2502    \u2502                    \u2502    \u2502                 \u2502\r\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n                                                            \u2502\r\n                                                            \u25bc\r\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n\u2502   HTTP Client   \u2502\u25c0\u2500\u2500\u2500\u2502   RESTHandler      \u2502\u25c0\u2500\u2500\u2500\u2502 RESTToolService \u2502\r\n\u2502   (httpx)       \u2502    \u2502                    \u2502    \u2502                 \u2502\r\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n                                                            \u2502\r\n                                                            \u25bc\r\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n\u2502   FastAPI       \u2502\u25c0\u2500\u2500\u2500\u2502 RESTToolRegistry   \u2502\u25c0\u2500\u2500\u2500\u2502   Tool Handler  \u2502\r\n\u2502   Endpoints     \u2502    \u2502                    \u2502    \u2502   Functions     \u2502\r\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n```\r\n\r\n### 2. Processing Steps\r\n\r\n1. **OpenAPI Parsing**: `OpenAPISpecParser` reads the specification and extracts operations\r\n2. **Operation Registration**: Each operation becomes a `ParsedOperation` with parameters and metadata\r\n3. **Tool Creation**: Operations are registered as FastAPI endpoints through `RESTToolService`\r\n4. **Registry Management**: `RESTToolRegistry` tracks all operations with filtering and statistics\r\n5. **Request Execution**: `RESTHandler` executes HTTP calls to target APIs\r\n6. **Response Handling**: Results are formatted and returned to the client\r\n\r\n### 3. Component Interaction\r\n\r\n```\r\nRequest \u2192 FastAPI Endpoint \u2192 Tool Handler \u2192 RESTHandler \u2192 Target API\r\n                                                 \u2193\r\nResponse \u2190 Tool Handler \u2190 RESTHandler \u2190 Target API Response\r\n```\r\n\r\n## Installation\r\n\r\n1. Install required dependencies:\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n2. Configure environment variables (optional):\r\n```bash\r\n# Copy the example environment file\r\ncp .env.example .env\r\n\r\n# Edit .env with your API keys and configuration\r\n# BEARER_AUTHENTICATION=your-api-key-here\r\n```\r\n\r\n## Quick Start\r\n\r\nGet started with REST tools in 3 simple steps:\r\n\r\n### 1. Run the Example\r\n```bash\r\n# Run the Optimizely API example\r\npython -m rest_tools_example\r\n```\r\n\r\n## How to Add New Tools\r\n\r\nThere are multiple ways to add new REST tools to the OPAL system, depending on your use case:\r\n\r\n### Method 1: From OpenAPI Specification URL\r\n\r\nThe easiest way to add tools from a publicly accessible OpenAPI spec:\r\n\r\n```python\r\nfrom fastapi import FastAPI\r\nfrom core.rest_tool_service import create_service_from_urls\r\nfrom core.rest_models import OpenAPIConfig\r\n\r\napp = FastAPI(title=\"My REST Tools Service\")\r\n\r\n# Configure which operations to include\r\nconfig = OpenAPIConfig(\r\n    base_url=\"https://api.example.com/v1\",  # Override spec base URL if needed\r\n    filter_operations=[\"list_users\", \"get_user\"],  # Only include these operations\r\n    # exclude_operations=[\"delete_all\"]  # Or exclude specific operations\r\n)\r\n\r\n# Create service from URL\r\nservice = await create_service_from_urls(\r\n    app, \r\n    {\"my-api\": \"https://api.example.com/openapi.json\"},  # spec_name: url\r\n    config\r\n)\r\n\r\nawait service.startup()\r\n```\r\n\r\n### Method 2: From Local OpenAPI Specification File\r\n\r\nWhen you have the OpenAPI spec as a local file:\r\n\r\n```python\r\nfrom pathlib import Path\r\nfrom core.rest_tool_service import create_rest_tool_service\r\n\r\n# Load from file path\r\nspec_path = Path(\"my-api-spec.json\")  # or .yaml\r\nservice = create_rest_tool_service(app, spec_path, config)\r\n\r\n# Or load and filter the spec first\r\nimport json\r\nwith open(\"full-api-spec.json\") as f:\r\n    full_spec = json.load(f)\r\n\r\n# Filter to specific paths\r\nfiltered_spec = {\r\n    **full_spec,\r\n    \"paths\": {path: methods for path, methods in full_spec[\"paths\"].items() \r\n              if path.startswith(\"/users\")}  # Only /users endpoints\r\n}\r\n\r\nservice = create_rest_tool_service(app, filtered_spec, config)\r\n```\r\n\r\n### Method 3: Multiple API Specifications\r\n\r\nWhen you need to combine multiple APIs into one service:\r\n\r\n```python\r\nfrom core.rest_tool_service import create_multi_spec_service\r\n\r\n# Multiple specs with names\r\nspecs = {\r\n    \"github-api\": github_openapi_spec,\r\n    \"slack-api\": slack_openapi_spec,\r\n    \"custom-api\": Path(\"custom-api.yaml\")\r\n}\r\n\r\nservice = create_multi_spec_service(app, specs, config)\r\nawait service.startup()\r\n\r\n# Access operations by spec\r\ngithub_ops = service.get_operations(spec_name=\"github-api\")\r\nslack_ops = service.get_operations(spec_name=\"slack-api\")\r\n```\r\n\r\n### Method 4: Dynamic Registration\r\n\r\nAdd specs dynamically to a running service:\r\n\r\n```python\r\n# Start with empty service\r\nservice = RESTToolService(app, config)\r\nawait service.startup()\r\n\r\n# Add specs as needed\r\ncount1 = service.register_openapi_spec(api_spec_1, \"api-v1\")\r\ncount2 = await service.register_openapi_url(\"https://api.example.com/spec\", \"api-v2\")\r\n\r\n# Bulk registration\r\nbatch_specs = [\r\n    {\"spec\": spec_1, \"name\": \"spec1\"},\r\n    {\"url\": \"https://api.example.com/spec2\", \"name\": \"spec2\"}\r\n]\r\nresults = service.register_multiple_specs(batch_specs)\r\n```\r\n\r\n### Configuration Options\r\n\r\nControl which operations become tools using `OpenAPIConfig`:\r\n\r\n```python\r\nconfig = OpenAPIConfig(\r\n    base_url=\"https://custom-base.com\",           # Override API base URL\r\n    filter_operations=[\"op1\", \"op2\"],            # Only include these operations\r\n    exclude_operations=[\"dangerous_op\"],          # Exclude specific operations\r\n    endpoint_prefix=\"/custom/api/path\",           # Custom endpoint prefix\r\n)\r\n```\r\n\r\n### Authentication Setup\r\n\r\nConfigure authentication for API calls:\r\n\r\n```python\r\n# In your tool calls, provide auth data:\r\nauth_data = {\r\n    # Direct headers\r\n    \"Authorization\": \"Bearer your-token\",\r\n    \"X-API-Key\": \"your-api-key\",\r\n    \r\n    # Or structured auth (matches OpenAPI security schemes)\r\n    \"api_key\": \"your-key\",\r\n    \"token\": \"bearer-token\",\r\n    \"access_token\": \"oauth-token\"\r\n}\r\n\r\n# The RESTHandler will automatically apply the appropriate headers\r\nresult = await service.rest_handler.execute_operation(\r\n    operation=my_operation,\r\n    parameters={\"param1\": \"value1\"},\r\n    auth_data=auth_data\r\n)\r\n```\r\n\r\n#### Environment Variables\r\n\r\nThe system supports configuration via environment variables:\r\n\r\n```bash\r\n# Set in your .env file or environment\r\n\r\n# Authentication\r\nBEARER_AUTHENTICATION=your-api-key-here\r\n\r\n# REST API Configuration\r\nRESTAPI_ENDPOINT_PREFIX=/rest-tools  # Default endpoint prefix for tools\r\n```\r\n\r\nConfiguration details:\r\n- `BEARER_AUTHENTICATION`: API authentication token (loaded by `get_auth_value()`)\r\n- `RESTAPI_ENDPOINT_PREFIX`: Configurable endpoint prefix for REST tools (default: `/rest-tools`)\r\n\r\n#### Configurable Endpoint Prefix\r\n\r\nYou can customize the endpoint prefix for REST tools using the `RESTAPI_ENDPOINT_PREFIX` environment variable:\r\n\r\n```bash\r\n# Examples:\r\nRESTAPI_ENDPOINT_PREFIX=/rest-tools           # Default\r\nRESTAPI_ENDPOINT_PREFIX=/experimental/rest-tools\r\nRESTAPI_ENDPOINT_PREFIX=/api/v2/tools\r\n```\r\n\r\nThis affects all tool endpoints. For example, with `/experimental/rest-tools`:\r\n- `list_projects` becomes `/experimental/rest-tools/list_projects`\r\n- `get_user` becomes `/experimental/rest-tools/get_user`\r\n\r\nYou can also override in code:\r\n\r\n```python\r\nconfig = OpenAPIConfig(\r\n    endpoint_prefix=\"/custom/api/path\"  # Overrides environment variable\r\n)\r\n```\r\n\r\n## Core Components\r\n\r\nThe system consists of several key components that work together to transform OpenAPI specs into executable tools:\r\n\r\n### 1. OpenAPISpecParser (`niteco/opal_rest_tool/openapi_parser.py:12`)\r\nParses OpenAPI 3.x specifications and extracts operations into `ParsedOperation` objects:\r\n\r\n**Key Features:**\r\n- Supports JSON and YAML OpenAPI specs\r\n- Extracts parameters from paths, query strings, and request bodies\r\n- Maps OpenAPI security schemes to authentication requirements, can omit this (for Opal Hackathon)\r\n- Applies operation filtering based on configuration\r\n\r\n```python\r\nfrom core.openapi_parser import OpenAPISpecParser\r\nfrom core.rest_models import OpenAPIConfig\r\n\r\nconfig = OpenAPIConfig(filter_operations=[\"list_users\", \"get_user\"])\r\nparser = OpenAPISpecParser(config)\r\noperations = parser.parse(openapi_spec)  # Returns List[ParsedOperation]\r\n```\r\n\r\n### 2. RESTHandler (`niteco/opal_rest_tool/rest_handler.py:13`)\r\nExecutes HTTP requests for parsed operations using the httpx client:\r\n\r\n**Key Features:**\r\n- Builds URLs with path parameter substitution\r\n- Handles bearer authentication\r\n- Separates parameters into query params and request body\r\n- Formats responses with error handling\r\n\r\n```python\r\nfrom core.rest_handler import RESTHandler\r\n\r\nhandler = RESTHandler(timeout=30)\r\nresult = await handler.execute_operation(\r\n    operation=parsed_operation,\r\n    parameters={\"user_id\": 123, \"include_details\": True},\r\n    auth_data={\"Authorization\": \"Bearer token\"}\r\n)\r\n```\r\n\r\n### 3. ParsedOperation (`niteco/opal_rest_tool/rest_models.py:10`)\r\nData model representing a parsed API operation with all necessary metadata:\r\n\r\n```python\r\n@dataclass\r\nclass ParsedOperation:\r\n    name: str                                    # Generated tool name (snake_case)\r\n    description: str                             # Operation description\r\n    method: str                                  # HTTP method (GET, POST, etc.)\r\n    path: str                                    # API path with parameters\r\n    base_url: str                                # Base URL for API calls\r\n    parameters: List[Parameter]                  # Extracted parameters\r\n    auth_requirements: Optional[List[AuthRequirement]]  # Auth requirements\r\n    operation_id: Optional[str]                  # Original OpenAPI operationId\r\n    request_body_schema: Optional[Dict[str, Any]]  # Request body schema\r\n    response_schema: Optional[Dict[str, Any]]    # Response schema\r\n    tags: Optional[List[str]]                    # OpenAPI tags\r\n```\r\n\r\n### 4. RESTToolService (`niteco/opal_rest_tool/rest_tool_service.py:117`)\r\nMain service class that extends the OPAL ToolsService to support REST API integration:\r\n\r\n**Key Features:**\r\n- Auto-registration of OpenAPI operations as FastAPI endpoints\r\n- Operation registry with filtering and management\r\n- Health monitoring and validation\r\n- Multi-spec support with bulk operations\r\n- Lifecycle management (startup/shutdown)\r\n\r\n```python\r\nfrom core.rest_tool_service import RESTToolService\r\nfrom core.rest_models import OpenAPIConfig\r\n\r\napp = FastAPI()\r\nconfig = OpenAPIConfig(base_url=\"https://api.example.com\")\r\nservice = RESTToolService(app, config)\r\n\r\n# Register specs\r\ncount = service.register_openapi_spec(openapi_spec, \"my-api\")\r\ncount = await service.register_openapi_url(\"https://api.example.com/spec\", \"api-v2\")\r\n\r\n# Manage operations\r\noperations = service.get_operations(spec_name=\"my-api\")\r\nservice.unregister_operation(\"unwanted_tool\")\r\n```\r\n\r\n### 5. RESTToolRegistry (`niteco/opal_rest_tool/rest_tool_service.py:26`)\r\nInternal registry that manages REST operations with metadata tracking:\r\n\r\n**Key Features:**\r\n- Operation storage with name-based lookup\r\n- Tag-based and spec-based filtering\r\n- Statistics and health monitoring\r\n- Bulk operations (clear, unregister)\r\n\r\n```python\r\n# Accessed through RESTToolService\r\nstats = service.registry.get_stats()\r\noperations_by_tag = service.registry.get_by_tag(\"users\")\r\noperations_by_spec = service.registry.get_by_spec(\"github-api\")\r\n```\r\n\r\n### 6. OpenAPIConfig (`niteco/opal_rest_tool/rest_models.py:38`)\r\nConfiguration object for customizing OpenAPI parsing and tool generation:\r\n\r\n```python\r\n@dataclass\r\nclass OpenAPIConfig:\r\n    base_url: Optional[str] = None                # Override API base URL\r\n    auth_config: Optional[Dict[str, Any]] = None  # Auth configuration\r\n    filter_operations: Optional[List[str]] = None # Include only these operations\r\n    exclude_operations: Optional[List[str]] = None # Exclude these operations\r\n```\r\n\r\n## Testing\r\n\r\nRun the test suite to see the RESTToolService in action:\r\n\r\n```bash\r\n# Test the parser only\r\npython test_rest_service.py\r\n# Choose option 1\r\n\r\n# Test the full service with HTTP calls\r\npython test_rest_service.py  \r\n# Choose option 2\r\n```\r\n\r\nThe test uses the JSONPlaceholder API (https://jsonplaceholder.typicode.com) to demonstrate:\r\n- GET requests with query parameters\r\n- POST requests with request body\r\n- Path parameter substitution\r\n- Response handling\r\n\r\n## Authentication Support\r\n\r\nThe RESTToolService supports bearer authentication method for now.\r\n\r\n## Error Handling\r\n\r\n- HTTP errors (4xx, 5xx) are captured and included in responses\r\n- Network errors are logged and re-raised as HTTPException\r\n- Invalid OpenAPI specs raise ValueError with details\r\n- Missing required parameters are logged as warnings\r\n\r\n## Discovery Endpoint\r\n\r\nAll generated tools are automatically discoverable via the `/discovery` endpoint:\r\n\r\n```json\r\n{\r\n  \"functions\": [\r\n    {\r\n      \"name\": \"get_users\",\r\n      \"description\": \"Get all users\",\r\n      \"parameters\": [...],\r\n      \"endpoint\": \"/rest-tools/get_users\",\r\n      \"http_method\": \"POST\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n## Limitations\r\n\r\n- Currently supports OpenAPI 3.x (not 2.x/Swagger)\r\n- Reference resolution is simplified (no external refs)\r\n- Authentication mapping may need customization for complex schemes\r\n- Response schemas are captured but not validated\r\n\r\n## Future Enhancements\r\n\r\n- Full $ref reference resolution\r\n- OpenAPI 2.x support\r\n- Response validation against schemas\r\n- Request/response transformation middleware\r\n- Rate limiting and retry logic\r\n- Caching for API responses\r\n\r\n## Project Structure\r\n\r\n### PyPI Package Structure (niteco.opal-rest-tool)\r\n```\r\nniteco/opal_rest_tool/                      # Core system components (included in PyPI)\r\n\u251c\u2500\u2500 __init__.py                             # Package initialization\r\n\u251c\u2500\u2500 rest_models.py                          # Data models (ParsedOperation, OpenAPIConfig)\r\n\u251c\u2500\u2500 openapi_parser.py                       # OpenAPI 3.x specification parser\r\n\u251c\u2500\u2500 rest_handler.py                         # HTTP client for REST API execution\r\n\u2514\u2500\u2500 rest_tool_service.py                    # Main service (RESTToolService, Registry)\r\n```\r\n\r\n### Development Repository Structure\r\n```\r\nNiteco.Opal-REST-tool/\r\n\u251c\u2500\u2500 niteco/opal_rest_tool/                  # Core system components (PyPI package)\r\n\u2502   \u251c\u2500\u2500 __init__.py                         # Package initialization\r\n\u2502   \u251c\u2500\u2500 rest_models.py                      # Data models (ParsedOperation, OpenAPIConfig)\r\n\u2502   \u251c\u2500\u2500 openapi_parser.py                   # OpenAPI 3.x specification parser\r\n\u2502   \u251c\u2500\u2500 rest_handler.py                     # HTTP client for REST API execution\r\n\u2502   \u2514\u2500\u2500 rest_tool_service.py               # Main service (RESTToolService, Registry)\r\n\u251c\u2500\u2500 rest_tools_example/                     # Example implementations (dev only)\r\n\u2502   \u251c\u2500\u2500 __init__.py                         # Package initialization\r\n\u2502   \u251c\u2500\u2500 __main__.py                         # Example entry point\r\n\u2502   \u251c\u2500\u2500 optimizely_web_experimentation.py   # Optimizely API integration example\r\n\u2502   \u2514\u2500\u2500 opal tool.postman_collection.json  # Postman collection for testing\r\n\u251c\u2500\u2500 test/                                   # Test files and specifications (dev only)\r\n\u2502   \u251c\u2500\u2500 Optimizely API - v1.0.json         # Sample OpenAPI specification\r\n\u2502   \u2514\u2500\u2500 test_rest.py                        # Comprehensive test suite\r\n\u251c\u2500\u2500 .env.example                            # Environment configuration template\r\n\u251c\u2500\u2500 pyproject.toml                          # Package configuration and PyPI metadata\r\n\u251c\u2500\u2500 requirements.txt                        # Python dependencies\r\n\u2514\u2500\u2500 README.md                              # This documentation\r\n```\r\n\r\n### Core Files Description\r\n\r\n- **`niteco/opal_rest_tool/rest_models.py`** - Data classes including `ParsedOperation` (line 10) and `OpenAPIConfig` (line 38)\r\n- **`niteco/opal_rest_tool/openapi_parser.py`** - OpenAPI spec parser with `OpenAPISpecParser` class (line 12) \r\n- **`niteco/opal_rest_tool/rest_handler.py`** - HTTP execution engine with `RESTHandler` class (line 13)\r\n- **`niteco/opal_rest_tool/rest_tool_service.py`** - Main service classes:\r\n  - `RESTToolRegistry` (line 26) - Operation registry management\r\n  - `RESTToolService` (line 117) - Main service extending OPAL ToolsService\r\n  - Factory functions (lines 503-571) - Convenience service creators\r\n- **`test/test_rest.py`** - Complete test suite demonstrating all features\r\n- **`rest_tools_example/optimizely_web_experimentation.py`** - Real-world example using Optimizely API\r\n\r\n## License\r\n\r\nThis implementation is part of the OPAL project.\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "OPAL REST Tool Service - Automatically generates tools from OpenAPI 3.x specifications",
    "version": "0.1.4",
    "project_urls": {
        "Documentation": "https://github.com/niteco/opal-rest-tool#readme",
        "Homepage": "https://github.com/niteco/opal-rest-tool",
        "Issues": "https://github.com/niteco/opal-rest-tool/issues",
        "Repository": "https://github.com/niteco/opal-rest-tool"
    },
    "split_keywords": [
        "openapi",
        " rest",
        " api",
        " tools",
        " opal",
        " fastapi"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "00305b7ace651ef445a020893ee6c507e8efb6a03e1a2e9d4c6010227b945cf8",
                "md5": "bc47f9ea8b599ec737947812493b9d92",
                "sha256": "ad925fab216cfac597513a02adea2af44170dcf3868171ce8ed8fbc0ff1af47f"
            },
            "downloads": -1,
            "filename": "niteco_opal_rest_tool-0.1.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "bc47f9ea8b599ec737947812493b9d92",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 19865,
            "upload_time": "2025-08-14T12:12:56",
            "upload_time_iso_8601": "2025-08-14T12:12:56.039208Z",
            "url": "https://files.pythonhosted.org/packages/00/30/5b7ace651ef445a020893ee6c507e8efb6a03e1a2e9d4c6010227b945cf8/niteco_opal_rest_tool-0.1.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "97cb326b12e96df6a873d416c75ab6a0929de4d6a9a3c1db8d488c93d88cac3a",
                "md5": "249016aa7796210e4417edc4b2605391",
                "sha256": "21b50f7823136b6250e618c19b418591fa64b3deecaacd7c69a5f56e5bc6ac8d"
            },
            "downloads": -1,
            "filename": "niteco_opal_rest_tool-0.1.4.tar.gz",
            "has_sig": false,
            "md5_digest": "249016aa7796210e4417edc4b2605391",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 23635,
            "upload_time": "2025-08-14T12:12:58",
            "upload_time_iso_8601": "2025-08-14T12:12:58.108142Z",
            "url": "https://files.pythonhosted.org/packages/97/cb/326b12e96df6a873d416c75ab6a0929de4d6a9a3c1db8d488c93d88cac3a/niteco_opal_rest_tool-0.1.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-14 12:12:58",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "niteco",
    "github_project": "opal-rest-tool#readme",
    "github_not_found": true,
    "lcname": "niteco.opal-rest-tool"
}
        
Elapsed time: 1.19814s