crmadminbrasil-dataverse-sdk


Namecrmadminbrasil-dataverse-sdk JSON
Version 1.1.4 PyPI version JSON
download
home_pageNone
SummaryAsync Python SDK for Microsoft Dataverse with enterprise features
upload_time2025-07-15 20:48:01
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords dataverse dynamics crm microsoft async sdk
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Microsoft Dataverse SDK for Python

[![PyPI version](https://badge.fury.io/py/crmadminbrasil-crmadminbrasil-dataverse-sdk.svg)](https://pypi.org/project/crmadminbrasil-crmadminbrasil-dataverse-sdk/)
[![Python Support](https://img.shields.io/pypi/pyversions/crmadminbrasil-crmadminbrasil-dataverse-sdk.svg)](https://pypi.org/project/crmadminbrasil-crmadminbrasil-dataverse-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Tests](https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/workflows/Tests/badge.svg)](https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/actions)
[![Coverage](https://codecov.io/gh/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/branch/main/graph/badge.svg)](https://codecov.io/gh/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk)

A comprehensive, enterprise-ready Python SDK for Microsoft Dataverse with async support, advanced features, and production-grade reliability.

## 🚀 Features

### Core Capabilities
- **100% Async Support**: Built with `httpx` and `asyncio` for high-performance async operations
- **Enterprise Ready**: Connection pooling, retry logic, rate limiting, and comprehensive error handling
- **Type Safety**: Full type hints with Pydantic models for strong typing and validation
- **Extensible**: Hook system for custom logging, telemetry, and request/response interceptors

### Operations
- **Complete CRUD**: Create, Read, Update, Delete, and Upsert operations
- **Bulk Operations**: High-performance batch processing with auto-chunking and parallel execution
- **Advanced Queries**: OData queries, FetchXML support, and intelligent pagination
- **Associations**: Entity relationship management (associate/disassociate)
- **Metadata**: Entity and attribute metadata retrieval
- **File Operations**: Attachment upload/download support

### Developer Experience
- **CLI Tool**: Full-featured command-line interface for all operations
- **Rich Documentation**: Comprehensive docs with examples and best practices
- **Testing**: Extensive test suite with unit and integration tests
- **CI/CD Ready**: GitHub Actions workflows and PyPI publishing automation

## 📦 Installation

### From PyPI (Recommended)
```bash
pip install crmadminbrasil-dataverse-sdk
```

### Development Installation
```bash
git clone https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk.git
cd crmadminbrasil-dataverse-sdk
pip install -e ".[dev]"
```

### Optional Dependencies
```bash
# For telemetry support
pip install "crmadminbrasil-dataverse-sdk[telemetry]"

# For documentation
pip install "crmadminbrasil-dataverse-sdk[docs]"

# All optional dependencies
pip install "crmadminbrasil-dataverse-sdk[dev,telemetry,docs]"
```

## 🔧 Quick Start

### Basic Usage

```python
import asyncio
from dataverse_sdk import DataverseSDK

async def main():
    # Initialize SDK
    sdk = DataverseSDK(
        dataverse_url="https://yourorg.crm.dynamics.com",
        client_id="your-client-id",
        client_secret="your-client-secret",
        tenant_id="your-tenant-id",
    )
    
    async with sdk:
        # Create an account
        account_data = {
            "name": "Contoso Ltd",
            "websiteurl": "https://contoso.com",
            "telephone1": "555-0123"
        }
        account_id = await sdk.create("accounts", account_data)
        print(f"Created account: {account_id}")
        
        # Query accounts
        accounts = await sdk.query("accounts", {
            "select": ["name", "websiteurl", "telephone1"],
            "filter": "statecode eq 0",
            "top": 10
        })
        
        for account in accounts.value:
            print(f"Account: {account['name']}")

if __name__ == "__main__":
    asyncio.run(main())
```

### Environment Configuration

Create a `.env` file:
```env
DATAVERSE_URL=https://yourorg.crm.dynamics.com
AZURE_CLIENT_ID=your-client-id
AZURE_CLIENT_SECRET=your-client-secret
AZURE_TENANT_ID=your-tenant-id
```

The SDK automatically loads environment variables:
```python
from dataverse_sdk import DataverseSDK

# Configuration loaded from environment
sdk = DataverseSDK()
```

## 📚 Documentation

### Table of Contents
- [Authentication](#authentication)
- [CRUD Operations](#crud-operations)
- [Query Operations](#query-operations)
- [Bulk Operations](#bulk-operations)
- [FetchXML Queries](#fetchxml-queries)
- [Entity Associations](#entity-associations)
- [Metadata Operations](#metadata-operations)
- [CLI Usage](#cli-usage)
- [Configuration](#configuration)
- [Error Handling](#error-handling)
- [Hooks and Extensibility](#hooks-and-extensibility)
- [Performance Optimization](#performance-optimization)
- [Testing](#testing)
- [Contributing](#contributing)




## 🔐 Authentication

The SDK supports multiple authentication flows for different scenarios:

### Client Credentials Flow (Service-to-Service)
Best for server applications and automation:

```python
from dataverse_sdk import DataverseSDK

sdk = DataverseSDK(
    dataverse_url="https://yourorg.crm.dynamics.com",
    client_id="your-client-id",
    client_secret="your-client-secret",
    tenant_id="your-tenant-id",
)

async with sdk:
    # SDK automatically uses client credentials flow
    accounts = await sdk.query("accounts", {"top": 5})
```

### Device Code Flow (CLI Applications)
Best for command-line tools and development:

```python
from dataverse_sdk.auth import DataverseAuthenticator

authenticator = DataverseAuthenticator(
    client_id="your-client-id",
    tenant_id="your-tenant-id",
    dataverse_url="https://yourorg.crm.dynamics.com",
)

# This will prompt user to visit a URL and enter a code
token = await authenticator.authenticate_device_code()
```

### Interactive Flow (Desktop Applications)
For applications with user interaction:

```python
# Interactive flow with local redirect
token = await authenticator.authenticate_interactive(
    redirect_uri="http://localhost:8080",
    port=8080
)
```

### Token Caching
The SDK automatically caches tokens to minimize authentication requests:

```python
# Tokens are cached automatically
sdk = DataverseSDK(...)

async with sdk:
    # First request authenticates and caches token
    await sdk.query("accounts", {"top": 1})
    
    # Subsequent requests use cached token
    await sdk.query("contacts", {"top": 1})
    
    # Clear cache if needed
    await sdk.clear_auth_cache()
```

## 📝 CRUD Operations

### Create Operations

```python
# Create a single entity
account_data = {
    "name": "Acme Corporation",
    "websiteurl": "https://acme.com",
    "telephone1": "555-0123",
    "description": "Leading provider of innovative solutions"
}

account_id = await sdk.create("accounts", account_data)
print(f"Created account: {account_id}")

# Create with return data
account = await sdk.create("accounts", account_data, return_record=True)
print(f"Created account: {account['name']} ({account['accountid']})")
```

### Read Operations

```python
# Read by ID
account = await sdk.read("accounts", account_id)
print(f"Account name: {account['name']}")

# Read with specific fields
account = await sdk.read(
    "accounts", 
    account_id,
    select=["name", "websiteurl", "telephone1"]
)

# Read with related entities
account = await sdk.read(
    "accounts",
    account_id,
    expand=["primarycontactid", "createdby"]
)
```

### Update Operations

```python
# Update entity
update_data = {
    "websiteurl": "https://newacme.com",
    "description": "Updated description"
}

await sdk.update("accounts", account_id, update_data)

# Update with return data
updated_account = await sdk.update(
    "accounts", 
    account_id, 
    update_data, 
    return_record=True
)
```

### Delete Operations

```python
# Delete entity
await sdk.delete("accounts", account_id)
```

### Upsert Operations

```python
# Upsert (create or update)
account_data = {
    "name": "Upsert Test Account",
    "websiteurl": "https://upsert.com"
}

result = await sdk.upsert("accounts", account_data)
if result.was_created:
    print(f"Created new account: {result.entity_id}")
else:
    print(f"Updated existing account: {result.entity_id}")

# Upsert with alternate key
result = await sdk.upsert(
    "accounts",
    account_data,
    alternate_key={"accountnumber": "ACC-001"}
)
```

## 🔍 Query Operations

### Basic Queries

```python
from dataverse_sdk.models import QueryOptions

# Simple query
accounts = await sdk.query("accounts", {
    "select": ["name", "websiteurl"],
    "top": 10
})

for account in accounts.value:
    print(f"{account['name']}: {account['websiteurl']}")

# Using QueryOptions model
options = QueryOptions(
    select=["name", "websiteurl", "telephone1"],
    filter="statecode eq 0",
    order_by=["name asc"],
    top=20
)

accounts = await sdk.query("accounts", options)
```

### Advanced Filtering

```python
# Complex filters
accounts = await sdk.query("accounts", {
    "select": ["name", "revenue"],
    "filter": "revenue gt 1000000 and statecode eq 0",
    "order_by": ["revenue desc"]
})

# String operations
accounts = await sdk.query("accounts", {
    "select": ["name"],
    "filter": "contains(name, 'Microsoft') or startswith(name, 'Contoso')"
})

# Date filtering
from datetime import datetime, timedelta

last_month = datetime.now() - timedelta(days=30)
recent_accounts = await sdk.query("accounts", {
    "select": ["name", "createdon"],
    "filter": f"createdon gt {last_month.isoformat()}"
})
```

### Pagination

```python
# Manual pagination
result = await sdk.query("accounts", {
    "select": ["name"],
    "top": 100
})

all_accounts = result.value
while result.has_more:
    # Get next page
    result = await sdk.query("accounts", {
        "select": ["name"],
        "top": 100,
        "skip": len(all_accounts)
    })
    all_accounts.extend(result.value)

# Automatic pagination
all_accounts = await sdk.query_all("accounts", {
    "select": ["name", "websiteurl"],
    "filter": "statecode eq 0"
}, max_records=1000)
```

### Related Entity Expansion

```python
# Expand related entities
accounts = await sdk.query("accounts", {
    "select": ["name", "websiteurl"],
    "expand": [
        "primarycontactid($select=fullname,emailaddress1)",
        "createdby($select=fullname)"
    ],
    "top": 10
})

for account in accounts.value:
    print(f"Account: {account['name']}")
    if account.get('primarycontactid'):
        contact = account['primarycontactid']
        print(f"  Primary Contact: {contact['fullname']}")
```

## ⚡ Bulk Operations

### Bulk Create

```python
# Prepare data
contacts = [
    {
        "firstname": "John",
        "lastname": "Doe",
        "emailaddress1": "john.doe@example.com"
    },
    {
        "firstname": "Jane",
        "lastname": "Smith",
        "emailaddress1": "jane.smith@example.com"
    },
    # ... more contacts
]

# Bulk create with automatic batching
result = await sdk.bulk_create(
    "contacts",
    contacts,
    batch_size=100,  # Process in batches of 100
    parallel=True    # Execute batches in parallel
)

print(f"Processed: {result.total_processed}")
print(f"Successful: {result.successful}")
print(f"Failed: {result.failed}")
print(f"Success rate: {result.success_rate:.1f}%")

if result.has_errors:
    print("Errors:")
    for error in result.errors[:5]:  # Show first 5 errors
        print(f"  - {error}")
```

### Bulk Update

```python
# Prepare updates (must include entity ID)
updates = [
    {
        "id": "contact-id-1",
        "jobtitle": "Senior Developer"
    },
    {
        "id": "contact-id-2", 
        "jobtitle": "Project Manager"
    },
    # ... more updates
]

result = await sdk.bulk_update("contacts", updates)
```

### Bulk Delete

```python
# Delete multiple entities
contact_ids = [
    "contact-id-1",
    "contact-id-2",
    "contact-id-3",
    # ... more IDs
]

result = await sdk.bulk_delete("contacts", contact_ids)
```

### Custom Batch Operations

```python
from dataverse_sdk.batch import BatchProcessor

# Create custom batch processor
batch_processor = BatchProcessor(
    client=sdk.client,
    default_batch_size=50,
    max_parallel_batches=3
)

# Custom operations
operations = [
    {
        "method": "POST",
        "url": "accounts",
        "body": {"name": "Account 1"}
    },
    {
        "method": "PATCH", 
        "url": "accounts(existing-id)",
        "body": {"description": "Updated"}
    },
    {
        "method": "DELETE",
        "url": "accounts(delete-id)"
    }
]

result = await batch_processor.execute_bulk_operation(
    operations,
    parallel=True,
    transactional=False
)
```

## 📊 FetchXML Queries

### Basic FetchXML

```python
# Execute FetchXML string
fetchxml = """
<fetch top="10">
    <entity name="account">
        <attribute name="name" />
        <attribute name="websiteurl" />
        <attribute name="telephone1" />
        <filter type="and">
            <condition attribute="statecode" operator="eq" value="0" />
            <condition attribute="revenue" operator="gt" value="1000000" />
        </filter>
        <order attribute="revenue" descending="true" />
    </entity>
</fetch>
"""

accounts = await sdk.fetch_xml(fetchxml)
for account in accounts:
    print(f"{account['name']}: {account.get('revenue', 'N/A')}")
```

### FetchXML with Linked Entities

```python
fetchxml = """
<fetch top="5">
    <entity name="account">
        <attribute name="name" />
        <attribute name="websiteurl" />
        <link-entity name="contact" from="parentcustomerid" to="accountid" alias="contact">
            <attribute name="fullname" />
            <attribute name="emailaddress1" />
            <filter type="and">
                <condition attribute="statecode" operator="eq" value="0" />
            </filter>
        </link-entity>
        <filter type="and">
            <condition attribute="statecode" operator="eq" value="0" />
        </filter>
    </entity>
</fetch>
"""

results = await sdk.fetch_xml(fetchxml)
```

### FetchXML Builder (Programmatic)

```python
from dataverse_sdk.models import FetchXMLQuery

# Build FetchXML programmatically
query = FetchXMLQuery(
    entity="account",
    attributes=["name", "websiteurl", "revenue"],
    filters=[{
        "type": "and",
        "conditions": [
            {"attribute": "statecode", "operator": "eq", "value": "0"},
            {"attribute": "revenue", "operator": "gt", "value": "1000000"}
        ]
    }],
    orders=[
        {"attribute": "revenue", "descending": True}
    ],
    top=10
)

# Convert to FetchXML and execute
fetchxml_string = query.to_fetchxml()
accounts = await sdk.fetch_xml(fetchxml_string)
```

## 🔗 Entity Associations

### Associate Entities

```python
# Associate account with contact
await sdk.associate(
    primary_entity_type="accounts",
    primary_entity_id=account_id,
    relationship_name="account_primary_contact",
    related_entity_type="contacts",
    related_entity_id=contact_id
)

# Many-to-many association
await sdk.associate(
    primary_entity_type="systemusers",
    primary_entity_id=user_id,
    relationship_name="systemuserroles_association",
    related_entity_type="roles",
    related_entity_id=role_id
)
```

### Disassociate Entities

```python
# Remove association
await sdk.disassociate(
    primary_entity_type="accounts",
    primary_entity_id=account_id,
    relationship_name="account_primary_contact"
)

# Remove specific many-to-many association
await sdk.disassociate(
    primary_entity_type="systemusers",
    primary_entity_id=user_id,
    relationship_name="systemuserroles_association",
    related_entity_id=role_id
)
```

## 🔍 Metadata Operations

### Entity Metadata

```python
# Get entity metadata
account_metadata = await sdk.get_entity_metadata("account")

print(f"Display Name: {account_metadata['DisplayName']['UserLocalizedLabel']['Label']}")
print(f"Logical Name: {account_metadata['LogicalName']}")
print(f"Primary Key: {account_metadata['PrimaryIdAttribute']}")
print(f"Primary Name: {account_metadata['PrimaryNameAttribute']}")

# List all attributes
for attr in account_metadata['Attributes']:
    print(f"  {attr['LogicalName']}: {attr['AttributeType']}")
```

### Attribute Metadata

```python
# Get specific attribute metadata
name_attr = await sdk.get_attribute_metadata("account", "name")

print(f"Display Name: {name_attr['DisplayName']['UserLocalizedLabel']['Label']}")
print(f"Type: {name_attr['AttributeType']}")
print(f"Max Length: {name_attr.get('MaxLength', 'N/A')}")
print(f"Required: {name_attr['RequiredLevel']['Value']}")
```

### Generate Entity Models

```python
# Generate Pydantic models from metadata (utility function)
from dataverse_sdk.utils import generate_entity_model

# This would generate a Pydantic model class
AccountModel = await generate_entity_model(sdk, "account")

# Use the generated model
account_data = {
    "name": "Test Account",
    "websiteurl": "https://test.com"
}

# Validate data with the model
account = AccountModel(**account_data)
```


## 🖥️ CLI Usage

The SDK includes a powerful command-line interface for all operations:

### Installation and Setup

```bash
# Install the SDK
pip install crmadminbrasil-dataverse-sdk

# Initialize configuration
dv-cli config init
# Follow prompts to enter your Dataverse URL, Client ID, etc.

# Test connection
dv-cli config test
```

### Entity Operations

```bash
# List entities
dv-cli entity list accounts --select name,websiteurl --top 10
dv-cli entity list contacts --filter "statecode eq 0" --order-by createdon

# Get specific entity
dv-cli entity get accounts 12345678-1234-1234-1234-123456789012

# Create entity
dv-cli entity create accounts --file account_data.json
echo '{"name": "CLI Test Account"}' | dv-cli entity create accounts

# Update entity
dv-cli entity update accounts 12345678-1234-1234-1234-123456789012 --file updates.json

# Delete entity
dv-cli entity delete accounts 12345678-1234-1234-1234-123456789012 --yes
```

### Bulk Operations

```bash
# Bulk create from JSON file
dv-cli bulk create contacts --file contacts.json --batch-size 100

# Bulk operations with progress
dv-cli bulk create accounts --file large_accounts.json --parallel
```

### Data Export/Import

```bash
# Export data
dv-cli data export accounts --output accounts_backup.json
dv-cli data export contacts --filter "statecode eq 0" --select firstname,lastname,emailaddress1

# Import data
dv-cli data import accounts --file accounts_backup.json
```

### FetchXML Operations

```bash
# Execute FetchXML from file
dv-cli fetchxml execute --file complex_query.xml

# Save FetchXML results
dv-cli fetchxml execute --file query.xml --output results.json
```

### Configuration Management

```bash
# View current configuration
dv-cli config show

# Update configuration
dv-cli config set dataverse_url https://neworg.crm.dynamics.com
dv-cli config set log_level DEBUG

# Use different config file
dv-cli --config-file prod-config.json entity list accounts
```

### Output Formats

```bash
# Table format (default)
dv-cli entity list accounts --top 5

# JSON format
dv-cli entity list accounts --top 5 --output json

# Save to file
dv-cli entity list accounts --output json > accounts.json
```

## ⚙️ Configuration

### Environment Variables

```bash
# Required
export DATAVERSE_URL="https://yourorg.crm.dynamics.com"
export AZURE_CLIENT_ID="your-client-id"
export AZURE_TENANT_ID="your-tenant-id"

# Optional
export AZURE_CLIENT_SECRET="your-client-secret"
export AZURE_AUTHORITY="https://login.microsoftonline.com/your-tenant-id"
export AZURE_SCOPE="https://yourorg.crm.dynamics.com/.default"

# SDK Configuration
export MAX_CONNECTIONS=100
export MAX_RETRIES=3
export DEFAULT_BATCH_SIZE=100
export LOG_LEVEL=INFO
```

### Configuration File

Create `dataverse-config.json`:

```json
{
  "dataverse_url": "https://yourorg.crm.dynamics.com",
  "client_id": "your-client-id",
  "client_secret": "your-client-secret",
  "tenant_id": "your-tenant-id",
  "max_connections": 100,
  "max_retries": 3,
  "default_batch_size": 100,
  "log_level": "INFO"
}
```

### Programmatic Configuration

```python
from dataverse_sdk import DataverseSDK
from dataverse_sdk.utils import Config

# Custom configuration
config = Config(
    max_connections=50,
    max_retries=5,
    default_batch_size=200,
    connect_timeout=15.0,
    read_timeout=60.0,
    debug=True
)

sdk = DataverseSDK(
    dataverse_url="https://yourorg.crm.dynamics.com",
    client_id="your-client-id",
    client_secret="your-client-secret", 
    tenant_id="your-tenant-id",
    config=config
)
```

### Multi-Environment Setup

```python
# Development environment
dev_sdk = DataverseSDK(
    dataverse_url="https://dev-org.crm.dynamics.com",
    client_id="dev-client-id",
    client_secret="dev-client-secret",
    tenant_id="dev-tenant-id"
)

# Production environment
prod_sdk = DataverseSDK(
    dataverse_url="https://prod-org.crm.dynamics.com",
    client_id="prod-client-id",
    client_secret="prod-client-secret",
    tenant_id="prod-tenant-id"
)

# Use different configurations
async def sync_data():
    async with dev_sdk as dev, prod_sdk as prod:
        # Get data from dev
        dev_accounts = await dev.query_all("accounts", {"select": ["name"]})
        
        # Create in prod
        await prod.bulk_create("accounts", dev_accounts)
```

## 🚨 Error Handling

### Exception Types

```python
from dataverse_sdk.exceptions import (
    DataverseSDKError,          # Base exception
    AuthenticationError,        # Authentication failures
    AuthorizationError,         # Permission issues
    ConnectionError,            # Network connectivity
    TimeoutError,              # Request timeouts
    RateLimitError,            # Rate limiting
    ValidationError,           # Data validation
    EntityNotFoundError,       # Entity not found
    APIError,                  # API errors
    BatchOperationError,       # Batch operation failures
)

# Specific error handling
try:
    account = await sdk.read("accounts", "invalid-id")
except EntityNotFoundError as e:
    print(f"Account not found: {e.entity_id}")
except AuthenticationError as e:
    print(f"Authentication failed: {e.message}")
except APIError as e:
    print(f"API error {e.status_code}: {e.message}")
    print(f"Error details: {e.response_data}")
```

### Retry and Rate Limiting

```python
from dataverse_sdk.exceptions import RateLimitError
import asyncio

async def robust_operation():
    max_attempts = 3
    attempt = 0
    
    while attempt < max_attempts:
        try:
            result = await sdk.query("accounts", {"top": 1000})
            return result
            
        except RateLimitError as e:
            attempt += 1
            if attempt >= max_attempts:
                raise
            
            # Wait for the suggested retry time
            wait_time = e.retry_after or 60
            print(f"Rate limited. Waiting {wait_time} seconds...")
            await asyncio.sleep(wait_time)
            
        except ConnectionError as e:
            attempt += 1
            if attempt >= max_attempts:
                raise
            
            # Exponential backoff for connection errors
            wait_time = 2 ** attempt
            print(f"Connection error. Retrying in {wait_time} seconds...")
            await asyncio.sleep(wait_time)
```

### Comprehensive Error Handling

```python
async def safe_bulk_operation(entities):
    try:
        result = await sdk.bulk_create("accounts", entities)
        
        if result.has_errors:
            print(f"Bulk operation completed with {result.failed} errors:")
            for error in result.errors:
                print(f"  - {error}")
        
        return result
        
    except BatchOperationError as e:
        print(f"Batch operation failed: {e.message}")
        print(f"Failed operations: {len(e.failed_operations)}")
        
        # Retry failed operations individually
        for failed_op in e.failed_operations:
            try:
                await sdk.create("accounts", failed_op["data"])
            except Exception as retry_error:
                print(f"Retry failed: {retry_error}")
                
    except Exception as e:
        print(f"Unexpected error: {e}")
        raise
```

## 🔌 Hooks and Extensibility

### Built-in Hooks

```python
from dataverse_sdk.hooks import (
    HookType,
    logging_hook,
    telemetry_hook,
    retry_logging_hook
)

# Register built-in hooks
sdk.register_hook(HookType.BEFORE_REQUEST, logging_hook)
sdk.register_hook(HookType.AFTER_RESPONSE, telemetry_hook)
sdk.register_hook(HookType.ON_RETRY, retry_logging_hook)
```

### Custom Hooks

```python
from dataverse_sdk.hooks import HookContext, HookType

def custom_request_hook(context: HookContext) -> None:
    """Custom hook to modify requests."""
    if context.hook_type == HookType.BEFORE_REQUEST:
        # Add custom headers
        context.request_data["headers"]["X-Custom-Header"] = "MyValue"
        
        # Log request details
        print(f"Making request to: {context.request_data['url']}")

def custom_response_hook(context: HookContext) -> None:
    """Custom hook to process responses."""
    if context.hook_type == HookType.AFTER_RESPONSE:
        # Log response time
        response_time = context.metadata.get("response_time", 0)
        print(f"Request completed in {response_time:.2f}s")
        
        # Store metrics
        context.set_custom_data("response_time", response_time)

# Register custom hooks
sdk.register_hook(HookType.BEFORE_REQUEST, custom_request_hook, priority=10)
sdk.register_hook(HookType.AFTER_RESPONSE, custom_response_hook)
```

### Async Hooks

```python
async def async_telemetry_hook(context: HookContext) -> None:
    """Async hook for telemetry."""
    if context.hook_type == HookType.AFTER_RESPONSE:
        # Send telemetry data asynchronously
        telemetry_data = {
            "url": context.request_data["url"],
            "method": context.request_data["method"],
            "status_code": context.response_data["status_code"],
            "response_time": context.metadata.get("response_time")
        }
        
        # Send to telemetry service (example)
        await send_telemetry(telemetry_data)

# Register async hook
sdk.register_hook(HookType.AFTER_RESPONSE, async_telemetry_hook)
```

### Hook Decorators

```python
from dataverse_sdk.hooks import hook, HookType

@hook(HookType.BEFORE_REQUEST, priority=5)
def request_validator(context: HookContext) -> None:
    """Validate requests before sending."""
    url = context.request_data["url"]
    method = context.request_data["method"]
    
    # Custom validation logic
    if method == "DELETE" and "accounts" in url:
        print("Warning: Deleting account!")

@hook(HookType.ON_ERROR)
def error_notifier(context: HookContext) -> None:
    """Notify on errors."""
    error = context.error
    url = context.request_data["url"]
    
    # Send notification (example)
    send_error_notification(f"Error in {url}: {error}")
```

### OpenTelemetry Integration

```python
from opentelemetry import trace
from dataverse_sdk.hooks import HookContext, HookType

tracer = trace.get_tracer(__name__)

def opentelemetry_hook(context: HookContext) -> None:
    """OpenTelemetry integration hook."""
    if context.hook_type == HookType.BEFORE_REQUEST:
        # Start span
        span = tracer.start_span(f"dataverse_{context.request_data['method']}")
        span.set_attribute("http.url", context.request_data["url"])
        span.set_attribute("http.method", context.request_data["method"])
        context.set_custom_data("span", span)
        
    elif context.hook_type == HookType.AFTER_RESPONSE:
        # End span
        span = context.get_custom_data("span")
        if span:
            span.set_attribute("http.status_code", context.response_data["status_code"])
            span.end()

# Register OpenTelemetry hook
sdk.register_hook(HookType.BEFORE_REQUEST, opentelemetry_hook)
sdk.register_hook(HookType.AFTER_RESPONSE, opentelemetry_hook)
```


## ⚡ Performance Optimization

### Connection Pooling

```python
from dataverse_sdk.utils import Config

# Optimize connection settings
config = Config(
    max_connections=200,           # Total connection pool size
    max_keepalive_connections=50,  # Keep-alive connections
    keepalive_expiry=30,          # Keep-alive timeout (seconds)
    connect_timeout=10.0,         # Connection timeout
    read_timeout=30.0,            # Read timeout
)

sdk = DataverseSDK(config=config)
```

### Batch Size Optimization

```python
# Optimize batch sizes based on data size
small_records = [...]  # Small records
large_records = [...]  # Records with many fields

# Use larger batches for small records
await sdk.bulk_create("contacts", small_records, batch_size=500)

# Use smaller batches for large records
await sdk.bulk_create("accounts", large_records, batch_size=50)
```

### Parallel Processing

```python
import asyncio

async def parallel_queries():
    # Execute multiple queries in parallel
    tasks = [
        sdk.query("accounts", {"select": ["name"], "top": 100}),
        sdk.query("contacts", {"select": ["fullname"], "top": 100}),
        sdk.query("opportunities", {"select": ["name"], "top": 100}),
    ]
    
    results = await asyncio.gather(*tasks)
    accounts, contacts, opportunities = results
    
    return {
        "accounts": accounts.value,
        "contacts": contacts.value,
        "opportunities": opportunities.value
    }

# Execute parallel queries
data = await parallel_queries()
```

### Memory-Efficient Streaming

```python
async def process_large_dataset():
    """Process large datasets efficiently."""
    page_size = 1000
    processed_count = 0
    
    # Process in chunks to avoid memory issues
    options = QueryOptions(
        select=["accountid", "name"],
        top=page_size
    )
    
    while True:
        result = await sdk.query("accounts", options)
        
        if not result.value:
            break
            
        # Process current batch
        for account in result.value:
            # Process individual account
            await process_account(account)
            processed_count += 1
        
        # Check for more data
        if not result.has_more:
            break
            
        # Update options for next page
        options.skip = processed_count
    
    print(f"Processed {processed_count} accounts")
```

### Caching Strategies

```python
from functools import lru_cache
import asyncio

class CachedDataverseSDK:
    def __init__(self, sdk):
        self.sdk = sdk
        self._metadata_cache = {}
    
    @lru_cache(maxsize=100)
    async def get_cached_entity_metadata(self, entity_type: str):
        """Cache entity metadata to avoid repeated API calls."""
        if entity_type not in self._metadata_cache:
            metadata = await self.sdk.get_entity_metadata(entity_type)
            self._metadata_cache[entity_type] = metadata
        
        return self._metadata_cache[entity_type]
    
    async def bulk_create_with_validation(self, entity_type: str, entities: list):
        """Bulk create with cached metadata validation."""
        # Get cached metadata
        metadata = await self.get_cached_entity_metadata(entity_type)
        
        # Validate entities against metadata
        validated_entities = []
        for entity in entities:
            if self._validate_entity(entity, metadata):
                validated_entities.append(entity)
        
        # Bulk create validated entities
        return await self.sdk.bulk_create(entity_type, validated_entities)

# Use cached SDK
cached_sdk = CachedDataverseSDK(sdk)
```

## 🧪 Testing

### Unit Tests

```python
import pytest
from unittest.mock import AsyncMock, patch
from dataverse_sdk import DataverseSDK

@pytest.mark.asyncio
async def test_account_creation():
    """Test account creation."""
    sdk = DataverseSDK(
        dataverse_url="https://test.crm.dynamics.com",
        client_id="test-client",
        tenant_id="test-tenant"
    )
    
    # Mock the create method
    sdk.create = AsyncMock(return_value="12345678-1234-1234-1234-123456789012")
    
    account_data = {"name": "Test Account"}
    account_id = await sdk.create("accounts", account_data)
    
    assert account_id == "12345678-1234-1234-1234-123456789012"
    sdk.create.assert_called_once_with("accounts", account_data)

@pytest.mark.asyncio
async def test_query_with_filter():
    """Test query with filter."""
    sdk = DataverseSDK(
        dataverse_url="https://test.crm.dynamics.com",
        client_id="test-client",
        tenant_id="test-tenant"
    )
    
    # Mock query response
    mock_response = {
        "value": [{"accountid": "123", "name": "Test Account"}],
        "@odata.count": 1
    }
    
    with patch.object(sdk, 'query', return_value=mock_response):
        result = await sdk.query("accounts", {"filter": "name eq 'Test'"})
        assert len(result["value"]) == 1
        assert result["value"][0]["name"] == "Test Account"
```

### Integration Tests

```python
import pytest
import os
from dataverse_sdk import DataverseSDK

# Skip if no credentials
pytestmark = pytest.mark.skipif(
    not os.getenv("DATAVERSE_URL"),
    reason="Integration tests require Dataverse credentials"
)

@pytest.fixture
async def sdk():
    """SDK fixture for integration tests."""
    sdk_instance = DataverseSDK()
    async with sdk_instance as sdk:
        yield sdk

@pytest.mark.asyncio
async def test_real_account_operations(sdk):
    """Test real account operations."""
    # Create test account
    account_data = {
        "name": "Integration Test Account",
        "description": "Created by integration test"
    }
    
    account_id = await sdk.create("accounts", account_data)
    
    try:
        # Read account
        account = await sdk.read("accounts", account_id)
        assert account["name"] == account_data["name"]
        
        # Update account
        await sdk.update("accounts", account_id, {"description": "Updated"})
        
        # Verify update
        updated_account = await sdk.read("accounts", account_id)
        assert updated_account["description"] == "Updated"
        
    finally:
        # Clean up
        await sdk.delete("accounts", account_id)
```

### Performance Tests

```python
import pytest
import time
from dataverse_sdk import DataverseSDK

@pytest.mark.slow
@pytest.mark.asyncio
async def test_bulk_operation_performance(sdk):
    """Test bulk operation performance."""
    # Create test data
    test_contacts = [
        {"firstname": f"Test{i}", "lastname": "Contact"}
        for i in range(100)
    ]
    
    start_time = time.time()
    
    # Bulk create
    result = await sdk.bulk_create("contacts", test_contacts)
    
    end_time = time.time()
    duration = end_time - start_time
    
    # Performance assertions
    assert duration < 30.0  # Should complete within 30 seconds
    assert result.success_rate > 90.0  # At least 90% success rate
    
    print(f"Bulk created {result.successful} contacts in {duration:.2f}s")
```

### Running Tests

```bash
# Install test dependencies
pip install -e ".[dev]"

# Run unit tests
pytest tests/unit/

# Run integration tests (requires credentials)
pytest tests/integration/

# Run with coverage
pytest --cov=dataverse_sdk --cov-report=html

# Run performance tests
pytest -m slow

# Run specific test
pytest tests/unit/test_auth.py::TestDataverseAuthenticator::test_client_credentials
```

## 📚 Advanced Examples

### Data Migration Script

```python
import asyncio
from dataverse_sdk import DataverseSDK

async def migrate_accounts():
    """Migrate accounts from one environment to another."""
    
    # Source environment
    source_sdk = DataverseSDK(
        dataverse_url="https://source.crm.dynamics.com",
        client_id="source-client-id",
        client_secret="source-secret",
        tenant_id="source-tenant"
    )
    
    # Target environment
    target_sdk = DataverseSDK(
        dataverse_url="https://target.crm.dynamics.com",
        client_id="target-client-id",
        client_secret="target-secret",
        tenant_id="target-tenant"
    )
    
    async with source_sdk as source, target_sdk as target:
        # Export accounts from source
        print("Exporting accounts from source...")
        accounts = await source.query_all("accounts", {
            "select": ["name", "websiteurl", "telephone1", "description"],
            "filter": "statecode eq 0"
        })
        
        print(f"Found {len(accounts)} accounts to migrate")
        
        # Import to target
        print("Importing accounts to target...")
        result = await target.bulk_create("accounts", accounts, batch_size=100)
        
        print(f"Migration completed:")
        print(f"  Successful: {result.successful}")
        print(f"  Failed: {result.failed}")
        print(f"  Success rate: {result.success_rate:.1f}%")

if __name__ == "__main__":
    asyncio.run(migrate_accounts())
```

### Data Synchronization

```python
import asyncio
from datetime import datetime, timedelta
from dataverse_sdk import DataverseSDK

class DataSynchronizer:
    def __init__(self, primary_sdk, secondary_sdk):
        self.primary = primary_sdk
        self.secondary = secondary_sdk
    
    async def sync_entity(self, entity_type: str, sync_field: str = "modifiedon"):
        """Sync entities based on modification date."""
        
        # Get last sync time (stored somewhere)
        last_sync = await self.get_last_sync_time(entity_type)
        
        # Query modified records from primary
        filter_expr = f"{sync_field} gt {last_sync.isoformat()}"
        modified_records = await self.primary.query_all(entity_type, {
            "filter": filter_expr,
            "order_by": [f"{sync_field} asc"]
        })
        
        if not modified_records:
            print(f"No modified {entity_type} found")
            return
        
        print(f"Syncing {len(modified_records)} modified {entity_type}")
        
        # Upsert to secondary (assuming alternate key exists)
        for record in modified_records:
            try:
                await self.secondary.upsert(
                    entity_type,
                    record,
                    alternate_key={"name": record["name"]}  # Adjust key as needed
                )
            except Exception as e:
                print(f"Failed to sync {record.get('name', 'unknown')}: {e}")
        
        # Update last sync time
        await self.update_last_sync_time(entity_type, datetime.now())
    
    async def get_last_sync_time(self, entity_type: str) -> datetime:
        """Get last sync time (implement based on your storage)."""
        # This could be stored in a database, file, or Dataverse itself
        return datetime.now() - timedelta(hours=1)  # Default to 1 hour ago
    
    async def update_last_sync_time(self, entity_type: str, sync_time: datetime):
        """Update last sync time (implement based on your storage)."""
        pass

async def run_sync():
    primary_sdk = DataverseSDK(...)  # Primary environment
    secondary_sdk = DataverseSDK(...)  # Secondary environment
    
    async with primary_sdk as primary, secondary_sdk as secondary:
        synchronizer = DataSynchronizer(primary, secondary)
        
        # Sync different entity types
        await synchronizer.sync_entity("accounts")
        await synchronizer.sync_entity("contacts")
        await synchronizer.sync_entity("opportunities")

if __name__ == "__main__":
    asyncio.run(run_sync())
```

### Custom Entity Manager

```python
from dataverse_sdk import DataverseSDK
from dataverse_sdk.models import Entity
from typing import List, Optional

class EntityManager:
    """High-level entity manager with business logic."""
    
    def __init__(self, sdk: DataverseSDK):
        self.sdk = sdk
    
    async def create_account_with_contacts(
        self,
        account_data: dict,
        contacts_data: List[dict]
    ) -> dict:
        """Create account with associated contacts."""
        
        # Create account
        account_id = await self.sdk.create("accounts", account_data)
        
        try:
            # Create contacts and associate with account
            contact_ids = []
            for contact_data in contacts_data:
                contact_data["parentcustomerid@odata.bind"] = f"accounts({account_id})"
                contact_id = await self.sdk.create("contacts", contact_data)
                contact_ids.append(contact_id)
            
            return {
                "account_id": account_id,
                "contact_ids": contact_ids,
                "success": True
            }
            
        except Exception as e:
            # Rollback: delete account if contact creation fails
            try:
                await self.sdk.delete("accounts", account_id)
            except:
                pass  # Ignore rollback errors
            
            raise e
    
    async def get_account_summary(self, account_id: str) -> dict:
        """Get comprehensive account summary."""
        
        # Get account with related data
        account = await self.sdk.read("accounts", account_id, expand=[
            "primarycontactid($select=fullname,emailaddress1)",
            "account_parent_account($select=name)",
            "contact_customer_accounts($select=fullname,emailaddress1;$top=5)"
        ])
        
        # Get additional statistics
        contact_count_result = await self.sdk.query("contacts", {
            "filter": f"parentcustomerid eq '{account_id}'",
            "count": True,
            "top": 0  # Just get count
        })
        
        opportunity_count_result = await self.sdk.query("opportunities", {
            "filter": f"customerid eq '{account_id}'",
            "count": True,
            "top": 0
        })
        
        return {
            "account": account,
            "contact_count": contact_count_result.total_count,
            "opportunity_count": opportunity_count_result.total_count,
            "primary_contact": account.get("primarycontactid"),
            "parent_account": account.get("account_parent_account"),
            "recent_contacts": account.get("contact_customer_accounts", [])
        }

# Usage
async def main():
    sdk = DataverseSDK(...)
    
    async with sdk:
        manager = EntityManager(sdk)
        
        # Create account with contacts
        result = await manager.create_account_with_contacts(
            account_data={"name": "Acme Corp", "websiteurl": "https://acme.com"},
            contacts_data=[
                {"firstname": "John", "lastname": "Doe", "emailaddress1": "john@acme.com"},
                {"firstname": "Jane", "lastname": "Smith", "emailaddress1": "jane@acme.com"}
            ]
        )
        
        # Get account summary
        summary = await manager.get_account_summary(result["account_id"])
        print(f"Account: {summary['account']['name']}")
        print(f"Contacts: {summary['contact_count']}")
        print(f"Opportunities: {summary['opportunity_count']}")
```

## 🤝 Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.

### Development Setup

```bash
# Clone the repository
git clone https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk.git
cd crmadminbrasil-dataverse-sdk

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install development dependencies
pip install -e ".[dev]"

# Install pre-commit hooks
pre-commit install

# Run tests
pytest

# Run linting
black dataverse_sdk/
isort dataverse_sdk/
flake8 dataverse_sdk/
mypy dataverse_sdk/
```

### Code Style

We use several tools to maintain code quality:

- **Black**: Code formatting
- **isort**: Import sorting
- **flake8**: Linting
- **mypy**: Type checking
- **bandit**: Security analysis

### Testing Guidelines

- Write tests for all new features
- Maintain test coverage above 90%
- Include both unit and integration tests
- Use descriptive test names and docstrings

### Documentation

- Update README.md for new features
- Add docstrings to all public methods
- Include type hints for all parameters and return values
- Provide usage examples

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🙏 Acknowledgments

- Microsoft Dataverse team for the excellent API
- The Python async community for inspiration
- All contributors who help improve this SDK

## 📞 Support

- **Documentation**: [https://crmadminbrasil-dataverse-sdk.readthedocs.io](https://crmadminbrasil-dataverse-sdk.readthedocs.io)
- **Issues**: [GitHub Issues](https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/issues)
- **Discussions**: [GitHub Discussions](https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/discussions)
- **Email**: support@crmadminbrasil-dataverse-sdk.com

## 🗺️ Roadmap

### Version 1.1
- [ ] WebSocket support for real-time notifications
- [ ] Enhanced FetchXML builder with GUI
- [ ] Plugin system for custom entity types
- [ ] Performance monitoring dashboard

### Version 1.2
- [ ] GraphQL-style query interface
- [ ] Built-in data validation rules
- [ ] Advanced caching strategies
- [ ] Multi-tenant management tools

### Version 2.0
- [ ] Support for Dataverse for Teams
- [ ] AI-powered query optimization
- [ ] Visual query builder
- [ ] Enterprise governance features

---

**Made with ❤️ by the Dataverse SDK Team**



## ⚡ **Performance & Benchmarks**

O SDK foi projetado para ser extremamente performático, capaz de lidar com milhões de registros em uso diário:

### **Configurações Otimizadas**
- **Batch Size**: > 100 registros por lote (padrão: 500)
- **Paralelismo**: Até 32 operações simultâneas
- **Pool de Conexões**: 100 conexões simultâneas
- **Throughput**: > 1000 registros/segundo

### **Testes de Performance**
```bash
# Executar benchmarks de performance
cd benchmarks/
pip install -r requirements.txt
python benchmark_bulk_create.py

# Stress test com milhões de registros
python stress_test.py
```

### **Resultados Típicos**
- ✅ **Criação em massa**: 1000+ registros/segundo
- ✅ **Consultas**: < 100ms para consultas simples
- ✅ **Bulk operations**: 10000+ registros/minuto
- ✅ **Memória**: < 500MB para 100k registros

## 📁 **Estrutura do Projeto**

```
dataverse-sdk/
├── 📦 dataverse_sdk/          # Código principal do SDK
├── 🖥️ cli/                    # Interface de linha de comando
├── 🧪 tests/                  # Testes unitários e integração
├── 📚 examples/               # Exemplos de uso
├── ⚡ benchmarks/             # Testes de performance
├── 🔧 scripts/                # Scripts utilitários
├── 📖 docs/                   # Documentação completa
│   ├── getting-started/       # Guias iniciais
│   ├── guides/                # Guias avançados
│   ├── tutorials/             # Tutoriais
│   ├── api-reference/         # Referência da API
│   ├── contributing/          # Guias de contribuição
│   ├── deployment/            # Guias de deployment
│   └── jekyll/                # Site GitHub Pages
└── 🤖 .github/               # Configurações GitHub
```

## 🔗 **Links da Documentação**

- **[📖 Documentação Completa](docs/)** - Toda a documentação organizada
- **[🚀 Início Rápido](docs/getting-started/quickstart.md)** - Primeiros passos
- **[🏢 Configuração Corporativa](docs/deployment/CORPORATE_SETUP_GUIDE.md)** - Para ambientes empresariais
- **[⚡ Benchmarks](benchmarks/)** - Testes de performance
- **[🤝 Contribuição](docs/contributing/CONTRIBUTING.md)** - Como contribuir
- **[📋 API Reference](docs/api-reference/dataverse-sdk.md)** - Documentação técnica


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "crmadminbrasil-dataverse-sdk",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "Dataverse SDK Team <team@dataverse-sdk.com>",
    "keywords": "dataverse, dynamics, crm, microsoft, async, sdk",
    "author": null,
    "author_email": "Dataverse SDK Team <team@dataverse-sdk.com>",
    "download_url": "https://files.pythonhosted.org/packages/fb/1e/e21921c5df048d3348be01b47f787d9a937939238ff99a60976b0bfde4c6/crmadminbrasil_dataverse_sdk-1.1.4.tar.gz",
    "platform": null,
    "description": "# Microsoft Dataverse SDK for Python\n\n[![PyPI version](https://badge.fury.io/py/crmadminbrasil-crmadminbrasil-dataverse-sdk.svg)](https://pypi.org/project/crmadminbrasil-crmadminbrasil-dataverse-sdk/)\n[![Python Support](https://img.shields.io/pypi/pyversions/crmadminbrasil-crmadminbrasil-dataverse-sdk.svg)](https://pypi.org/project/crmadminbrasil-crmadminbrasil-dataverse-sdk/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Tests](https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/workflows/Tests/badge.svg)](https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/actions)\n[![Coverage](https://codecov.io/gh/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/branch/main/graph/badge.svg)](https://codecov.io/gh/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk)\n\nA comprehensive, enterprise-ready Python SDK for Microsoft Dataverse with async support, advanced features, and production-grade reliability.\n\n## \ud83d\ude80 Features\n\n### Core Capabilities\n- **100% Async Support**: Built with `httpx` and `asyncio` for high-performance async operations\n- **Enterprise Ready**: Connection pooling, retry logic, rate limiting, and comprehensive error handling\n- **Type Safety**: Full type hints with Pydantic models for strong typing and validation\n- **Extensible**: Hook system for custom logging, telemetry, and request/response interceptors\n\n### Operations\n- **Complete CRUD**: Create, Read, Update, Delete, and Upsert operations\n- **Bulk Operations**: High-performance batch processing with auto-chunking and parallel execution\n- **Advanced Queries**: OData queries, FetchXML support, and intelligent pagination\n- **Associations**: Entity relationship management (associate/disassociate)\n- **Metadata**: Entity and attribute metadata retrieval\n- **File Operations**: Attachment upload/download support\n\n### Developer Experience\n- **CLI Tool**: Full-featured command-line interface for all operations\n- **Rich Documentation**: Comprehensive docs with examples and best practices\n- **Testing**: Extensive test suite with unit and integration tests\n- **CI/CD Ready**: GitHub Actions workflows and PyPI publishing automation\n\n## \ud83d\udce6 Installation\n\n### From PyPI (Recommended)\n```bash\npip install crmadminbrasil-dataverse-sdk\n```\n\n### Development Installation\n```bash\ngit clone https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk.git\ncd crmadminbrasil-dataverse-sdk\npip install -e \".[dev]\"\n```\n\n### Optional Dependencies\n```bash\n# For telemetry support\npip install \"crmadminbrasil-dataverse-sdk[telemetry]\"\n\n# For documentation\npip install \"crmadminbrasil-dataverse-sdk[docs]\"\n\n# All optional dependencies\npip install \"crmadminbrasil-dataverse-sdk[dev,telemetry,docs]\"\n```\n\n## \ud83d\udd27 Quick Start\n\n### Basic Usage\n\n```python\nimport asyncio\nfrom dataverse_sdk import DataverseSDK\n\nasync def main():\n    # Initialize SDK\n    sdk = DataverseSDK(\n        dataverse_url=\"https://yourorg.crm.dynamics.com\",\n        client_id=\"your-client-id\",\n        client_secret=\"your-client-secret\",\n        tenant_id=\"your-tenant-id\",\n    )\n    \n    async with sdk:\n        # Create an account\n        account_data = {\n            \"name\": \"Contoso Ltd\",\n            \"websiteurl\": \"https://contoso.com\",\n            \"telephone1\": \"555-0123\"\n        }\n        account_id = await sdk.create(\"accounts\", account_data)\n        print(f\"Created account: {account_id}\")\n        \n        # Query accounts\n        accounts = await sdk.query(\"accounts\", {\n            \"select\": [\"name\", \"websiteurl\", \"telephone1\"],\n            \"filter\": \"statecode eq 0\",\n            \"top\": 10\n        })\n        \n        for account in accounts.value:\n            print(f\"Account: {account['name']}\")\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n### Environment Configuration\n\nCreate a `.env` file:\n```env\nDATAVERSE_URL=https://yourorg.crm.dynamics.com\nAZURE_CLIENT_ID=your-client-id\nAZURE_CLIENT_SECRET=your-client-secret\nAZURE_TENANT_ID=your-tenant-id\n```\n\nThe SDK automatically loads environment variables:\n```python\nfrom dataverse_sdk import DataverseSDK\n\n# Configuration loaded from environment\nsdk = DataverseSDK()\n```\n\n## \ud83d\udcda Documentation\n\n### Table of Contents\n- [Authentication](#authentication)\n- [CRUD Operations](#crud-operations)\n- [Query Operations](#query-operations)\n- [Bulk Operations](#bulk-operations)\n- [FetchXML Queries](#fetchxml-queries)\n- [Entity Associations](#entity-associations)\n- [Metadata Operations](#metadata-operations)\n- [CLI Usage](#cli-usage)\n- [Configuration](#configuration)\n- [Error Handling](#error-handling)\n- [Hooks and Extensibility](#hooks-and-extensibility)\n- [Performance Optimization](#performance-optimization)\n- [Testing](#testing)\n- [Contributing](#contributing)\n\n\n\n\n## \ud83d\udd10 Authentication\n\nThe SDK supports multiple authentication flows for different scenarios:\n\n### Client Credentials Flow (Service-to-Service)\nBest for server applications and automation:\n\n```python\nfrom dataverse_sdk import DataverseSDK\n\nsdk = DataverseSDK(\n    dataverse_url=\"https://yourorg.crm.dynamics.com\",\n    client_id=\"your-client-id\",\n    client_secret=\"your-client-secret\",\n    tenant_id=\"your-tenant-id\",\n)\n\nasync with sdk:\n    # SDK automatically uses client credentials flow\n    accounts = await sdk.query(\"accounts\", {\"top\": 5})\n```\n\n### Device Code Flow (CLI Applications)\nBest for command-line tools and development:\n\n```python\nfrom dataverse_sdk.auth import DataverseAuthenticator\n\nauthenticator = DataverseAuthenticator(\n    client_id=\"your-client-id\",\n    tenant_id=\"your-tenant-id\",\n    dataverse_url=\"https://yourorg.crm.dynamics.com\",\n)\n\n# This will prompt user to visit a URL and enter a code\ntoken = await authenticator.authenticate_device_code()\n```\n\n### Interactive Flow (Desktop Applications)\nFor applications with user interaction:\n\n```python\n# Interactive flow with local redirect\ntoken = await authenticator.authenticate_interactive(\n    redirect_uri=\"http://localhost:8080\",\n    port=8080\n)\n```\n\n### Token Caching\nThe SDK automatically caches tokens to minimize authentication requests:\n\n```python\n# Tokens are cached automatically\nsdk = DataverseSDK(...)\n\nasync with sdk:\n    # First request authenticates and caches token\n    await sdk.query(\"accounts\", {\"top\": 1})\n    \n    # Subsequent requests use cached token\n    await sdk.query(\"contacts\", {\"top\": 1})\n    \n    # Clear cache if needed\n    await sdk.clear_auth_cache()\n```\n\n## \ud83d\udcdd CRUD Operations\n\n### Create Operations\n\n```python\n# Create a single entity\naccount_data = {\n    \"name\": \"Acme Corporation\",\n    \"websiteurl\": \"https://acme.com\",\n    \"telephone1\": \"555-0123\",\n    \"description\": \"Leading provider of innovative solutions\"\n}\n\naccount_id = await sdk.create(\"accounts\", account_data)\nprint(f\"Created account: {account_id}\")\n\n# Create with return data\naccount = await sdk.create(\"accounts\", account_data, return_record=True)\nprint(f\"Created account: {account['name']} ({account['accountid']})\")\n```\n\n### Read Operations\n\n```python\n# Read by ID\naccount = await sdk.read(\"accounts\", account_id)\nprint(f\"Account name: {account['name']}\")\n\n# Read with specific fields\naccount = await sdk.read(\n    \"accounts\", \n    account_id,\n    select=[\"name\", \"websiteurl\", \"telephone1\"]\n)\n\n# Read with related entities\naccount = await sdk.read(\n    \"accounts\",\n    account_id,\n    expand=[\"primarycontactid\", \"createdby\"]\n)\n```\n\n### Update Operations\n\n```python\n# Update entity\nupdate_data = {\n    \"websiteurl\": \"https://newacme.com\",\n    \"description\": \"Updated description\"\n}\n\nawait sdk.update(\"accounts\", account_id, update_data)\n\n# Update with return data\nupdated_account = await sdk.update(\n    \"accounts\", \n    account_id, \n    update_data, \n    return_record=True\n)\n```\n\n### Delete Operations\n\n```python\n# Delete entity\nawait sdk.delete(\"accounts\", account_id)\n```\n\n### Upsert Operations\n\n```python\n# Upsert (create or update)\naccount_data = {\n    \"name\": \"Upsert Test Account\",\n    \"websiteurl\": \"https://upsert.com\"\n}\n\nresult = await sdk.upsert(\"accounts\", account_data)\nif result.was_created:\n    print(f\"Created new account: {result.entity_id}\")\nelse:\n    print(f\"Updated existing account: {result.entity_id}\")\n\n# Upsert with alternate key\nresult = await sdk.upsert(\n    \"accounts\",\n    account_data,\n    alternate_key={\"accountnumber\": \"ACC-001\"}\n)\n```\n\n## \ud83d\udd0d Query Operations\n\n### Basic Queries\n\n```python\nfrom dataverse_sdk.models import QueryOptions\n\n# Simple query\naccounts = await sdk.query(\"accounts\", {\n    \"select\": [\"name\", \"websiteurl\"],\n    \"top\": 10\n})\n\nfor account in accounts.value:\n    print(f\"{account['name']}: {account['websiteurl']}\")\n\n# Using QueryOptions model\noptions = QueryOptions(\n    select=[\"name\", \"websiteurl\", \"telephone1\"],\n    filter=\"statecode eq 0\",\n    order_by=[\"name asc\"],\n    top=20\n)\n\naccounts = await sdk.query(\"accounts\", options)\n```\n\n### Advanced Filtering\n\n```python\n# Complex filters\naccounts = await sdk.query(\"accounts\", {\n    \"select\": [\"name\", \"revenue\"],\n    \"filter\": \"revenue gt 1000000 and statecode eq 0\",\n    \"order_by\": [\"revenue desc\"]\n})\n\n# String operations\naccounts = await sdk.query(\"accounts\", {\n    \"select\": [\"name\"],\n    \"filter\": \"contains(name, 'Microsoft') or startswith(name, 'Contoso')\"\n})\n\n# Date filtering\nfrom datetime import datetime, timedelta\n\nlast_month = datetime.now() - timedelta(days=30)\nrecent_accounts = await sdk.query(\"accounts\", {\n    \"select\": [\"name\", \"createdon\"],\n    \"filter\": f\"createdon gt {last_month.isoformat()}\"\n})\n```\n\n### Pagination\n\n```python\n# Manual pagination\nresult = await sdk.query(\"accounts\", {\n    \"select\": [\"name\"],\n    \"top\": 100\n})\n\nall_accounts = result.value\nwhile result.has_more:\n    # Get next page\n    result = await sdk.query(\"accounts\", {\n        \"select\": [\"name\"],\n        \"top\": 100,\n        \"skip\": len(all_accounts)\n    })\n    all_accounts.extend(result.value)\n\n# Automatic pagination\nall_accounts = await sdk.query_all(\"accounts\", {\n    \"select\": [\"name\", \"websiteurl\"],\n    \"filter\": \"statecode eq 0\"\n}, max_records=1000)\n```\n\n### Related Entity Expansion\n\n```python\n# Expand related entities\naccounts = await sdk.query(\"accounts\", {\n    \"select\": [\"name\", \"websiteurl\"],\n    \"expand\": [\n        \"primarycontactid($select=fullname,emailaddress1)\",\n        \"createdby($select=fullname)\"\n    ],\n    \"top\": 10\n})\n\nfor account in accounts.value:\n    print(f\"Account: {account['name']}\")\n    if account.get('primarycontactid'):\n        contact = account['primarycontactid']\n        print(f\"  Primary Contact: {contact['fullname']}\")\n```\n\n## \u26a1 Bulk Operations\n\n### Bulk Create\n\n```python\n# Prepare data\ncontacts = [\n    {\n        \"firstname\": \"John\",\n        \"lastname\": \"Doe\",\n        \"emailaddress1\": \"john.doe@example.com\"\n    },\n    {\n        \"firstname\": \"Jane\",\n        \"lastname\": \"Smith\",\n        \"emailaddress1\": \"jane.smith@example.com\"\n    },\n    # ... more contacts\n]\n\n# Bulk create with automatic batching\nresult = await sdk.bulk_create(\n    \"contacts\",\n    contacts,\n    batch_size=100,  # Process in batches of 100\n    parallel=True    # Execute batches in parallel\n)\n\nprint(f\"Processed: {result.total_processed}\")\nprint(f\"Successful: {result.successful}\")\nprint(f\"Failed: {result.failed}\")\nprint(f\"Success rate: {result.success_rate:.1f}%\")\n\nif result.has_errors:\n    print(\"Errors:\")\n    for error in result.errors[:5]:  # Show first 5 errors\n        print(f\"  - {error}\")\n```\n\n### Bulk Update\n\n```python\n# Prepare updates (must include entity ID)\nupdates = [\n    {\n        \"id\": \"contact-id-1\",\n        \"jobtitle\": \"Senior Developer\"\n    },\n    {\n        \"id\": \"contact-id-2\", \n        \"jobtitle\": \"Project Manager\"\n    },\n    # ... more updates\n]\n\nresult = await sdk.bulk_update(\"contacts\", updates)\n```\n\n### Bulk Delete\n\n```python\n# Delete multiple entities\ncontact_ids = [\n    \"contact-id-1\",\n    \"contact-id-2\",\n    \"contact-id-3\",\n    # ... more IDs\n]\n\nresult = await sdk.bulk_delete(\"contacts\", contact_ids)\n```\n\n### Custom Batch Operations\n\n```python\nfrom dataverse_sdk.batch import BatchProcessor\n\n# Create custom batch processor\nbatch_processor = BatchProcessor(\n    client=sdk.client,\n    default_batch_size=50,\n    max_parallel_batches=3\n)\n\n# Custom operations\noperations = [\n    {\n        \"method\": \"POST\",\n        \"url\": \"accounts\",\n        \"body\": {\"name\": \"Account 1\"}\n    },\n    {\n        \"method\": \"PATCH\", \n        \"url\": \"accounts(existing-id)\",\n        \"body\": {\"description\": \"Updated\"}\n    },\n    {\n        \"method\": \"DELETE\",\n        \"url\": \"accounts(delete-id)\"\n    }\n]\n\nresult = await batch_processor.execute_bulk_operation(\n    operations,\n    parallel=True,\n    transactional=False\n)\n```\n\n## \ud83d\udcca FetchXML Queries\n\n### Basic FetchXML\n\n```python\n# Execute FetchXML string\nfetchxml = \"\"\"\n<fetch top=\"10\">\n    <entity name=\"account\">\n        <attribute name=\"name\" />\n        <attribute name=\"websiteurl\" />\n        <attribute name=\"telephone1\" />\n        <filter type=\"and\">\n            <condition attribute=\"statecode\" operator=\"eq\" value=\"0\" />\n            <condition attribute=\"revenue\" operator=\"gt\" value=\"1000000\" />\n        </filter>\n        <order attribute=\"revenue\" descending=\"true\" />\n    </entity>\n</fetch>\n\"\"\"\n\naccounts = await sdk.fetch_xml(fetchxml)\nfor account in accounts:\n    print(f\"{account['name']}: {account.get('revenue', 'N/A')}\")\n```\n\n### FetchXML with Linked Entities\n\n```python\nfetchxml = \"\"\"\n<fetch top=\"5\">\n    <entity name=\"account\">\n        <attribute name=\"name\" />\n        <attribute name=\"websiteurl\" />\n        <link-entity name=\"contact\" from=\"parentcustomerid\" to=\"accountid\" alias=\"contact\">\n            <attribute name=\"fullname\" />\n            <attribute name=\"emailaddress1\" />\n            <filter type=\"and\">\n                <condition attribute=\"statecode\" operator=\"eq\" value=\"0\" />\n            </filter>\n        </link-entity>\n        <filter type=\"and\">\n            <condition attribute=\"statecode\" operator=\"eq\" value=\"0\" />\n        </filter>\n    </entity>\n</fetch>\n\"\"\"\n\nresults = await sdk.fetch_xml(fetchxml)\n```\n\n### FetchXML Builder (Programmatic)\n\n```python\nfrom dataverse_sdk.models import FetchXMLQuery\n\n# Build FetchXML programmatically\nquery = FetchXMLQuery(\n    entity=\"account\",\n    attributes=[\"name\", \"websiteurl\", \"revenue\"],\n    filters=[{\n        \"type\": \"and\",\n        \"conditions\": [\n            {\"attribute\": \"statecode\", \"operator\": \"eq\", \"value\": \"0\"},\n            {\"attribute\": \"revenue\", \"operator\": \"gt\", \"value\": \"1000000\"}\n        ]\n    }],\n    orders=[\n        {\"attribute\": \"revenue\", \"descending\": True}\n    ],\n    top=10\n)\n\n# Convert to FetchXML and execute\nfetchxml_string = query.to_fetchxml()\naccounts = await sdk.fetch_xml(fetchxml_string)\n```\n\n## \ud83d\udd17 Entity Associations\n\n### Associate Entities\n\n```python\n# Associate account with contact\nawait sdk.associate(\n    primary_entity_type=\"accounts\",\n    primary_entity_id=account_id,\n    relationship_name=\"account_primary_contact\",\n    related_entity_type=\"contacts\",\n    related_entity_id=contact_id\n)\n\n# Many-to-many association\nawait sdk.associate(\n    primary_entity_type=\"systemusers\",\n    primary_entity_id=user_id,\n    relationship_name=\"systemuserroles_association\",\n    related_entity_type=\"roles\",\n    related_entity_id=role_id\n)\n```\n\n### Disassociate Entities\n\n```python\n# Remove association\nawait sdk.disassociate(\n    primary_entity_type=\"accounts\",\n    primary_entity_id=account_id,\n    relationship_name=\"account_primary_contact\"\n)\n\n# Remove specific many-to-many association\nawait sdk.disassociate(\n    primary_entity_type=\"systemusers\",\n    primary_entity_id=user_id,\n    relationship_name=\"systemuserroles_association\",\n    related_entity_id=role_id\n)\n```\n\n## \ud83d\udd0d Metadata Operations\n\n### Entity Metadata\n\n```python\n# Get entity metadata\naccount_metadata = await sdk.get_entity_metadata(\"account\")\n\nprint(f\"Display Name: {account_metadata['DisplayName']['UserLocalizedLabel']['Label']}\")\nprint(f\"Logical Name: {account_metadata['LogicalName']}\")\nprint(f\"Primary Key: {account_metadata['PrimaryIdAttribute']}\")\nprint(f\"Primary Name: {account_metadata['PrimaryNameAttribute']}\")\n\n# List all attributes\nfor attr in account_metadata['Attributes']:\n    print(f\"  {attr['LogicalName']}: {attr['AttributeType']}\")\n```\n\n### Attribute Metadata\n\n```python\n# Get specific attribute metadata\nname_attr = await sdk.get_attribute_metadata(\"account\", \"name\")\n\nprint(f\"Display Name: {name_attr['DisplayName']['UserLocalizedLabel']['Label']}\")\nprint(f\"Type: {name_attr['AttributeType']}\")\nprint(f\"Max Length: {name_attr.get('MaxLength', 'N/A')}\")\nprint(f\"Required: {name_attr['RequiredLevel']['Value']}\")\n```\n\n### Generate Entity Models\n\n```python\n# Generate Pydantic models from metadata (utility function)\nfrom dataverse_sdk.utils import generate_entity_model\n\n# This would generate a Pydantic model class\nAccountModel = await generate_entity_model(sdk, \"account\")\n\n# Use the generated model\naccount_data = {\n    \"name\": \"Test Account\",\n    \"websiteurl\": \"https://test.com\"\n}\n\n# Validate data with the model\naccount = AccountModel(**account_data)\n```\n\n\n## \ud83d\udda5\ufe0f CLI Usage\n\nThe SDK includes a powerful command-line interface for all operations:\n\n### Installation and Setup\n\n```bash\n# Install the SDK\npip install crmadminbrasil-dataverse-sdk\n\n# Initialize configuration\ndv-cli config init\n# Follow prompts to enter your Dataverse URL, Client ID, etc.\n\n# Test connection\ndv-cli config test\n```\n\n### Entity Operations\n\n```bash\n# List entities\ndv-cli entity list accounts --select name,websiteurl --top 10\ndv-cli entity list contacts --filter \"statecode eq 0\" --order-by createdon\n\n# Get specific entity\ndv-cli entity get accounts 12345678-1234-1234-1234-123456789012\n\n# Create entity\ndv-cli entity create accounts --file account_data.json\necho '{\"name\": \"CLI Test Account\"}' | dv-cli entity create accounts\n\n# Update entity\ndv-cli entity update accounts 12345678-1234-1234-1234-123456789012 --file updates.json\n\n# Delete entity\ndv-cli entity delete accounts 12345678-1234-1234-1234-123456789012 --yes\n```\n\n### Bulk Operations\n\n```bash\n# Bulk create from JSON file\ndv-cli bulk create contacts --file contacts.json --batch-size 100\n\n# Bulk operations with progress\ndv-cli bulk create accounts --file large_accounts.json --parallel\n```\n\n### Data Export/Import\n\n```bash\n# Export data\ndv-cli data export accounts --output accounts_backup.json\ndv-cli data export contacts --filter \"statecode eq 0\" --select firstname,lastname,emailaddress1\n\n# Import data\ndv-cli data import accounts --file accounts_backup.json\n```\n\n### FetchXML Operations\n\n```bash\n# Execute FetchXML from file\ndv-cli fetchxml execute --file complex_query.xml\n\n# Save FetchXML results\ndv-cli fetchxml execute --file query.xml --output results.json\n```\n\n### Configuration Management\n\n```bash\n# View current configuration\ndv-cli config show\n\n# Update configuration\ndv-cli config set dataverse_url https://neworg.crm.dynamics.com\ndv-cli config set log_level DEBUG\n\n# Use different config file\ndv-cli --config-file prod-config.json entity list accounts\n```\n\n### Output Formats\n\n```bash\n# Table format (default)\ndv-cli entity list accounts --top 5\n\n# JSON format\ndv-cli entity list accounts --top 5 --output json\n\n# Save to file\ndv-cli entity list accounts --output json > accounts.json\n```\n\n## \u2699\ufe0f Configuration\n\n### Environment Variables\n\n```bash\n# Required\nexport DATAVERSE_URL=\"https://yourorg.crm.dynamics.com\"\nexport AZURE_CLIENT_ID=\"your-client-id\"\nexport AZURE_TENANT_ID=\"your-tenant-id\"\n\n# Optional\nexport AZURE_CLIENT_SECRET=\"your-client-secret\"\nexport AZURE_AUTHORITY=\"https://login.microsoftonline.com/your-tenant-id\"\nexport AZURE_SCOPE=\"https://yourorg.crm.dynamics.com/.default\"\n\n# SDK Configuration\nexport MAX_CONNECTIONS=100\nexport MAX_RETRIES=3\nexport DEFAULT_BATCH_SIZE=100\nexport LOG_LEVEL=INFO\n```\n\n### Configuration File\n\nCreate `dataverse-config.json`:\n\n```json\n{\n  \"dataverse_url\": \"https://yourorg.crm.dynamics.com\",\n  \"client_id\": \"your-client-id\",\n  \"client_secret\": \"your-client-secret\",\n  \"tenant_id\": \"your-tenant-id\",\n  \"max_connections\": 100,\n  \"max_retries\": 3,\n  \"default_batch_size\": 100,\n  \"log_level\": \"INFO\"\n}\n```\n\n### Programmatic Configuration\n\n```python\nfrom dataverse_sdk import DataverseSDK\nfrom dataverse_sdk.utils import Config\n\n# Custom configuration\nconfig = Config(\n    max_connections=50,\n    max_retries=5,\n    default_batch_size=200,\n    connect_timeout=15.0,\n    read_timeout=60.0,\n    debug=True\n)\n\nsdk = DataverseSDK(\n    dataverse_url=\"https://yourorg.crm.dynamics.com\",\n    client_id=\"your-client-id\",\n    client_secret=\"your-client-secret\", \n    tenant_id=\"your-tenant-id\",\n    config=config\n)\n```\n\n### Multi-Environment Setup\n\n```python\n# Development environment\ndev_sdk = DataverseSDK(\n    dataverse_url=\"https://dev-org.crm.dynamics.com\",\n    client_id=\"dev-client-id\",\n    client_secret=\"dev-client-secret\",\n    tenant_id=\"dev-tenant-id\"\n)\n\n# Production environment\nprod_sdk = DataverseSDK(\n    dataverse_url=\"https://prod-org.crm.dynamics.com\",\n    client_id=\"prod-client-id\",\n    client_secret=\"prod-client-secret\",\n    tenant_id=\"prod-tenant-id\"\n)\n\n# Use different configurations\nasync def sync_data():\n    async with dev_sdk as dev, prod_sdk as prod:\n        # Get data from dev\n        dev_accounts = await dev.query_all(\"accounts\", {\"select\": [\"name\"]})\n        \n        # Create in prod\n        await prod.bulk_create(\"accounts\", dev_accounts)\n```\n\n## \ud83d\udea8 Error Handling\n\n### Exception Types\n\n```python\nfrom dataverse_sdk.exceptions import (\n    DataverseSDKError,          # Base exception\n    AuthenticationError,        # Authentication failures\n    AuthorizationError,         # Permission issues\n    ConnectionError,            # Network connectivity\n    TimeoutError,              # Request timeouts\n    RateLimitError,            # Rate limiting\n    ValidationError,           # Data validation\n    EntityNotFoundError,       # Entity not found\n    APIError,                  # API errors\n    BatchOperationError,       # Batch operation failures\n)\n\n# Specific error handling\ntry:\n    account = await sdk.read(\"accounts\", \"invalid-id\")\nexcept EntityNotFoundError as e:\n    print(f\"Account not found: {e.entity_id}\")\nexcept AuthenticationError as e:\n    print(f\"Authentication failed: {e.message}\")\nexcept APIError as e:\n    print(f\"API error {e.status_code}: {e.message}\")\n    print(f\"Error details: {e.response_data}\")\n```\n\n### Retry and Rate Limiting\n\n```python\nfrom dataverse_sdk.exceptions import RateLimitError\nimport asyncio\n\nasync def robust_operation():\n    max_attempts = 3\n    attempt = 0\n    \n    while attempt < max_attempts:\n        try:\n            result = await sdk.query(\"accounts\", {\"top\": 1000})\n            return result\n            \n        except RateLimitError as e:\n            attempt += 1\n            if attempt >= max_attempts:\n                raise\n            \n            # Wait for the suggested retry time\n            wait_time = e.retry_after or 60\n            print(f\"Rate limited. Waiting {wait_time} seconds...\")\n            await asyncio.sleep(wait_time)\n            \n        except ConnectionError as e:\n            attempt += 1\n            if attempt >= max_attempts:\n                raise\n            \n            # Exponential backoff for connection errors\n            wait_time = 2 ** attempt\n            print(f\"Connection error. Retrying in {wait_time} seconds...\")\n            await asyncio.sleep(wait_time)\n```\n\n### Comprehensive Error Handling\n\n```python\nasync def safe_bulk_operation(entities):\n    try:\n        result = await sdk.bulk_create(\"accounts\", entities)\n        \n        if result.has_errors:\n            print(f\"Bulk operation completed with {result.failed} errors:\")\n            for error in result.errors:\n                print(f\"  - {error}\")\n        \n        return result\n        \n    except BatchOperationError as e:\n        print(f\"Batch operation failed: {e.message}\")\n        print(f\"Failed operations: {len(e.failed_operations)}\")\n        \n        # Retry failed operations individually\n        for failed_op in e.failed_operations:\n            try:\n                await sdk.create(\"accounts\", failed_op[\"data\"])\n            except Exception as retry_error:\n                print(f\"Retry failed: {retry_error}\")\n                \n    except Exception as e:\n        print(f\"Unexpected error: {e}\")\n        raise\n```\n\n## \ud83d\udd0c Hooks and Extensibility\n\n### Built-in Hooks\n\n```python\nfrom dataverse_sdk.hooks import (\n    HookType,\n    logging_hook,\n    telemetry_hook,\n    retry_logging_hook\n)\n\n# Register built-in hooks\nsdk.register_hook(HookType.BEFORE_REQUEST, logging_hook)\nsdk.register_hook(HookType.AFTER_RESPONSE, telemetry_hook)\nsdk.register_hook(HookType.ON_RETRY, retry_logging_hook)\n```\n\n### Custom Hooks\n\n```python\nfrom dataverse_sdk.hooks import HookContext, HookType\n\ndef custom_request_hook(context: HookContext) -> None:\n    \"\"\"Custom hook to modify requests.\"\"\"\n    if context.hook_type == HookType.BEFORE_REQUEST:\n        # Add custom headers\n        context.request_data[\"headers\"][\"X-Custom-Header\"] = \"MyValue\"\n        \n        # Log request details\n        print(f\"Making request to: {context.request_data['url']}\")\n\ndef custom_response_hook(context: HookContext) -> None:\n    \"\"\"Custom hook to process responses.\"\"\"\n    if context.hook_type == HookType.AFTER_RESPONSE:\n        # Log response time\n        response_time = context.metadata.get(\"response_time\", 0)\n        print(f\"Request completed in {response_time:.2f}s\")\n        \n        # Store metrics\n        context.set_custom_data(\"response_time\", response_time)\n\n# Register custom hooks\nsdk.register_hook(HookType.BEFORE_REQUEST, custom_request_hook, priority=10)\nsdk.register_hook(HookType.AFTER_RESPONSE, custom_response_hook)\n```\n\n### Async Hooks\n\n```python\nasync def async_telemetry_hook(context: HookContext) -> None:\n    \"\"\"Async hook for telemetry.\"\"\"\n    if context.hook_type == HookType.AFTER_RESPONSE:\n        # Send telemetry data asynchronously\n        telemetry_data = {\n            \"url\": context.request_data[\"url\"],\n            \"method\": context.request_data[\"method\"],\n            \"status_code\": context.response_data[\"status_code\"],\n            \"response_time\": context.metadata.get(\"response_time\")\n        }\n        \n        # Send to telemetry service (example)\n        await send_telemetry(telemetry_data)\n\n# Register async hook\nsdk.register_hook(HookType.AFTER_RESPONSE, async_telemetry_hook)\n```\n\n### Hook Decorators\n\n```python\nfrom dataverse_sdk.hooks import hook, HookType\n\n@hook(HookType.BEFORE_REQUEST, priority=5)\ndef request_validator(context: HookContext) -> None:\n    \"\"\"Validate requests before sending.\"\"\"\n    url = context.request_data[\"url\"]\n    method = context.request_data[\"method\"]\n    \n    # Custom validation logic\n    if method == \"DELETE\" and \"accounts\" in url:\n        print(\"Warning: Deleting account!\")\n\n@hook(HookType.ON_ERROR)\ndef error_notifier(context: HookContext) -> None:\n    \"\"\"Notify on errors.\"\"\"\n    error = context.error\n    url = context.request_data[\"url\"]\n    \n    # Send notification (example)\n    send_error_notification(f\"Error in {url}: {error}\")\n```\n\n### OpenTelemetry Integration\n\n```python\nfrom opentelemetry import trace\nfrom dataverse_sdk.hooks import HookContext, HookType\n\ntracer = trace.get_tracer(__name__)\n\ndef opentelemetry_hook(context: HookContext) -> None:\n    \"\"\"OpenTelemetry integration hook.\"\"\"\n    if context.hook_type == HookType.BEFORE_REQUEST:\n        # Start span\n        span = tracer.start_span(f\"dataverse_{context.request_data['method']}\")\n        span.set_attribute(\"http.url\", context.request_data[\"url\"])\n        span.set_attribute(\"http.method\", context.request_data[\"method\"])\n        context.set_custom_data(\"span\", span)\n        \n    elif context.hook_type == HookType.AFTER_RESPONSE:\n        # End span\n        span = context.get_custom_data(\"span\")\n        if span:\n            span.set_attribute(\"http.status_code\", context.response_data[\"status_code\"])\n            span.end()\n\n# Register OpenTelemetry hook\nsdk.register_hook(HookType.BEFORE_REQUEST, opentelemetry_hook)\nsdk.register_hook(HookType.AFTER_RESPONSE, opentelemetry_hook)\n```\n\n\n## \u26a1 Performance Optimization\n\n### Connection Pooling\n\n```python\nfrom dataverse_sdk.utils import Config\n\n# Optimize connection settings\nconfig = Config(\n    max_connections=200,           # Total connection pool size\n    max_keepalive_connections=50,  # Keep-alive connections\n    keepalive_expiry=30,          # Keep-alive timeout (seconds)\n    connect_timeout=10.0,         # Connection timeout\n    read_timeout=30.0,            # Read timeout\n)\n\nsdk = DataverseSDK(config=config)\n```\n\n### Batch Size Optimization\n\n```python\n# Optimize batch sizes based on data size\nsmall_records = [...]  # Small records\nlarge_records = [...]  # Records with many fields\n\n# Use larger batches for small records\nawait sdk.bulk_create(\"contacts\", small_records, batch_size=500)\n\n# Use smaller batches for large records\nawait sdk.bulk_create(\"accounts\", large_records, batch_size=50)\n```\n\n### Parallel Processing\n\n```python\nimport asyncio\n\nasync def parallel_queries():\n    # Execute multiple queries in parallel\n    tasks = [\n        sdk.query(\"accounts\", {\"select\": [\"name\"], \"top\": 100}),\n        sdk.query(\"contacts\", {\"select\": [\"fullname\"], \"top\": 100}),\n        sdk.query(\"opportunities\", {\"select\": [\"name\"], \"top\": 100}),\n    ]\n    \n    results = await asyncio.gather(*tasks)\n    accounts, contacts, opportunities = results\n    \n    return {\n        \"accounts\": accounts.value,\n        \"contacts\": contacts.value,\n        \"opportunities\": opportunities.value\n    }\n\n# Execute parallel queries\ndata = await parallel_queries()\n```\n\n### Memory-Efficient Streaming\n\n```python\nasync def process_large_dataset():\n    \"\"\"Process large datasets efficiently.\"\"\"\n    page_size = 1000\n    processed_count = 0\n    \n    # Process in chunks to avoid memory issues\n    options = QueryOptions(\n        select=[\"accountid\", \"name\"],\n        top=page_size\n    )\n    \n    while True:\n        result = await sdk.query(\"accounts\", options)\n        \n        if not result.value:\n            break\n            \n        # Process current batch\n        for account in result.value:\n            # Process individual account\n            await process_account(account)\n            processed_count += 1\n        \n        # Check for more data\n        if not result.has_more:\n            break\n            \n        # Update options for next page\n        options.skip = processed_count\n    \n    print(f\"Processed {processed_count} accounts\")\n```\n\n### Caching Strategies\n\n```python\nfrom functools import lru_cache\nimport asyncio\n\nclass CachedDataverseSDK:\n    def __init__(self, sdk):\n        self.sdk = sdk\n        self._metadata_cache = {}\n    \n    @lru_cache(maxsize=100)\n    async def get_cached_entity_metadata(self, entity_type: str):\n        \"\"\"Cache entity metadata to avoid repeated API calls.\"\"\"\n        if entity_type not in self._metadata_cache:\n            metadata = await self.sdk.get_entity_metadata(entity_type)\n            self._metadata_cache[entity_type] = metadata\n        \n        return self._metadata_cache[entity_type]\n    \n    async def bulk_create_with_validation(self, entity_type: str, entities: list):\n        \"\"\"Bulk create with cached metadata validation.\"\"\"\n        # Get cached metadata\n        metadata = await self.get_cached_entity_metadata(entity_type)\n        \n        # Validate entities against metadata\n        validated_entities = []\n        for entity in entities:\n            if self._validate_entity(entity, metadata):\n                validated_entities.append(entity)\n        \n        # Bulk create validated entities\n        return await self.sdk.bulk_create(entity_type, validated_entities)\n\n# Use cached SDK\ncached_sdk = CachedDataverseSDK(sdk)\n```\n\n## \ud83e\uddea Testing\n\n### Unit Tests\n\n```python\nimport pytest\nfrom unittest.mock import AsyncMock, patch\nfrom dataverse_sdk import DataverseSDK\n\n@pytest.mark.asyncio\nasync def test_account_creation():\n    \"\"\"Test account creation.\"\"\"\n    sdk = DataverseSDK(\n        dataverse_url=\"https://test.crm.dynamics.com\",\n        client_id=\"test-client\",\n        tenant_id=\"test-tenant\"\n    )\n    \n    # Mock the create method\n    sdk.create = AsyncMock(return_value=\"12345678-1234-1234-1234-123456789012\")\n    \n    account_data = {\"name\": \"Test Account\"}\n    account_id = await sdk.create(\"accounts\", account_data)\n    \n    assert account_id == \"12345678-1234-1234-1234-123456789012\"\n    sdk.create.assert_called_once_with(\"accounts\", account_data)\n\n@pytest.mark.asyncio\nasync def test_query_with_filter():\n    \"\"\"Test query with filter.\"\"\"\n    sdk = DataverseSDK(\n        dataverse_url=\"https://test.crm.dynamics.com\",\n        client_id=\"test-client\",\n        tenant_id=\"test-tenant\"\n    )\n    \n    # Mock query response\n    mock_response = {\n        \"value\": [{\"accountid\": \"123\", \"name\": \"Test Account\"}],\n        \"@odata.count\": 1\n    }\n    \n    with patch.object(sdk, 'query', return_value=mock_response):\n        result = await sdk.query(\"accounts\", {\"filter\": \"name eq 'Test'\"})\n        assert len(result[\"value\"]) == 1\n        assert result[\"value\"][0][\"name\"] == \"Test Account\"\n```\n\n### Integration Tests\n\n```python\nimport pytest\nimport os\nfrom dataverse_sdk import DataverseSDK\n\n# Skip if no credentials\npytestmark = pytest.mark.skipif(\n    not os.getenv(\"DATAVERSE_URL\"),\n    reason=\"Integration tests require Dataverse credentials\"\n)\n\n@pytest.fixture\nasync def sdk():\n    \"\"\"SDK fixture for integration tests.\"\"\"\n    sdk_instance = DataverseSDK()\n    async with sdk_instance as sdk:\n        yield sdk\n\n@pytest.mark.asyncio\nasync def test_real_account_operations(sdk):\n    \"\"\"Test real account operations.\"\"\"\n    # Create test account\n    account_data = {\n        \"name\": \"Integration Test Account\",\n        \"description\": \"Created by integration test\"\n    }\n    \n    account_id = await sdk.create(\"accounts\", account_data)\n    \n    try:\n        # Read account\n        account = await sdk.read(\"accounts\", account_id)\n        assert account[\"name\"] == account_data[\"name\"]\n        \n        # Update account\n        await sdk.update(\"accounts\", account_id, {\"description\": \"Updated\"})\n        \n        # Verify update\n        updated_account = await sdk.read(\"accounts\", account_id)\n        assert updated_account[\"description\"] == \"Updated\"\n        \n    finally:\n        # Clean up\n        await sdk.delete(\"accounts\", account_id)\n```\n\n### Performance Tests\n\n```python\nimport pytest\nimport time\nfrom dataverse_sdk import DataverseSDK\n\n@pytest.mark.slow\n@pytest.mark.asyncio\nasync def test_bulk_operation_performance(sdk):\n    \"\"\"Test bulk operation performance.\"\"\"\n    # Create test data\n    test_contacts = [\n        {\"firstname\": f\"Test{i}\", \"lastname\": \"Contact\"}\n        for i in range(100)\n    ]\n    \n    start_time = time.time()\n    \n    # Bulk create\n    result = await sdk.bulk_create(\"contacts\", test_contacts)\n    \n    end_time = time.time()\n    duration = end_time - start_time\n    \n    # Performance assertions\n    assert duration < 30.0  # Should complete within 30 seconds\n    assert result.success_rate > 90.0  # At least 90% success rate\n    \n    print(f\"Bulk created {result.successful} contacts in {duration:.2f}s\")\n```\n\n### Running Tests\n\n```bash\n# Install test dependencies\npip install -e \".[dev]\"\n\n# Run unit tests\npytest tests/unit/\n\n# Run integration tests (requires credentials)\npytest tests/integration/\n\n# Run with coverage\npytest --cov=dataverse_sdk --cov-report=html\n\n# Run performance tests\npytest -m slow\n\n# Run specific test\npytest tests/unit/test_auth.py::TestDataverseAuthenticator::test_client_credentials\n```\n\n## \ud83d\udcda Advanced Examples\n\n### Data Migration Script\n\n```python\nimport asyncio\nfrom dataverse_sdk import DataverseSDK\n\nasync def migrate_accounts():\n    \"\"\"Migrate accounts from one environment to another.\"\"\"\n    \n    # Source environment\n    source_sdk = DataverseSDK(\n        dataverse_url=\"https://source.crm.dynamics.com\",\n        client_id=\"source-client-id\",\n        client_secret=\"source-secret\",\n        tenant_id=\"source-tenant\"\n    )\n    \n    # Target environment\n    target_sdk = DataverseSDK(\n        dataverse_url=\"https://target.crm.dynamics.com\",\n        client_id=\"target-client-id\",\n        client_secret=\"target-secret\",\n        tenant_id=\"target-tenant\"\n    )\n    \n    async with source_sdk as source, target_sdk as target:\n        # Export accounts from source\n        print(\"Exporting accounts from source...\")\n        accounts = await source.query_all(\"accounts\", {\n            \"select\": [\"name\", \"websiteurl\", \"telephone1\", \"description\"],\n            \"filter\": \"statecode eq 0\"\n        })\n        \n        print(f\"Found {len(accounts)} accounts to migrate\")\n        \n        # Import to target\n        print(\"Importing accounts to target...\")\n        result = await target.bulk_create(\"accounts\", accounts, batch_size=100)\n        \n        print(f\"Migration completed:\")\n        print(f\"  Successful: {result.successful}\")\n        print(f\"  Failed: {result.failed}\")\n        print(f\"  Success rate: {result.success_rate:.1f}%\")\n\nif __name__ == \"__main__\":\n    asyncio.run(migrate_accounts())\n```\n\n### Data Synchronization\n\n```python\nimport asyncio\nfrom datetime import datetime, timedelta\nfrom dataverse_sdk import DataverseSDK\n\nclass DataSynchronizer:\n    def __init__(self, primary_sdk, secondary_sdk):\n        self.primary = primary_sdk\n        self.secondary = secondary_sdk\n    \n    async def sync_entity(self, entity_type: str, sync_field: str = \"modifiedon\"):\n        \"\"\"Sync entities based on modification date.\"\"\"\n        \n        # Get last sync time (stored somewhere)\n        last_sync = await self.get_last_sync_time(entity_type)\n        \n        # Query modified records from primary\n        filter_expr = f\"{sync_field} gt {last_sync.isoformat()}\"\n        modified_records = await self.primary.query_all(entity_type, {\n            \"filter\": filter_expr,\n            \"order_by\": [f\"{sync_field} asc\"]\n        })\n        \n        if not modified_records:\n            print(f\"No modified {entity_type} found\")\n            return\n        \n        print(f\"Syncing {len(modified_records)} modified {entity_type}\")\n        \n        # Upsert to secondary (assuming alternate key exists)\n        for record in modified_records:\n            try:\n                await self.secondary.upsert(\n                    entity_type,\n                    record,\n                    alternate_key={\"name\": record[\"name\"]}  # Adjust key as needed\n                )\n            except Exception as e:\n                print(f\"Failed to sync {record.get('name', 'unknown')}: {e}\")\n        \n        # Update last sync time\n        await self.update_last_sync_time(entity_type, datetime.now())\n    \n    async def get_last_sync_time(self, entity_type: str) -> datetime:\n        \"\"\"Get last sync time (implement based on your storage).\"\"\"\n        # This could be stored in a database, file, or Dataverse itself\n        return datetime.now() - timedelta(hours=1)  # Default to 1 hour ago\n    \n    async def update_last_sync_time(self, entity_type: str, sync_time: datetime):\n        \"\"\"Update last sync time (implement based on your storage).\"\"\"\n        pass\n\nasync def run_sync():\n    primary_sdk = DataverseSDK(...)  # Primary environment\n    secondary_sdk = DataverseSDK(...)  # Secondary environment\n    \n    async with primary_sdk as primary, secondary_sdk as secondary:\n        synchronizer = DataSynchronizer(primary, secondary)\n        \n        # Sync different entity types\n        await synchronizer.sync_entity(\"accounts\")\n        await synchronizer.sync_entity(\"contacts\")\n        await synchronizer.sync_entity(\"opportunities\")\n\nif __name__ == \"__main__\":\n    asyncio.run(run_sync())\n```\n\n### Custom Entity Manager\n\n```python\nfrom dataverse_sdk import DataverseSDK\nfrom dataverse_sdk.models import Entity\nfrom typing import List, Optional\n\nclass EntityManager:\n    \"\"\"High-level entity manager with business logic.\"\"\"\n    \n    def __init__(self, sdk: DataverseSDK):\n        self.sdk = sdk\n    \n    async def create_account_with_contacts(\n        self,\n        account_data: dict,\n        contacts_data: List[dict]\n    ) -> dict:\n        \"\"\"Create account with associated contacts.\"\"\"\n        \n        # Create account\n        account_id = await self.sdk.create(\"accounts\", account_data)\n        \n        try:\n            # Create contacts and associate with account\n            contact_ids = []\n            for contact_data in contacts_data:\n                contact_data[\"parentcustomerid@odata.bind\"] = f\"accounts({account_id})\"\n                contact_id = await self.sdk.create(\"contacts\", contact_data)\n                contact_ids.append(contact_id)\n            \n            return {\n                \"account_id\": account_id,\n                \"contact_ids\": contact_ids,\n                \"success\": True\n            }\n            \n        except Exception as e:\n            # Rollback: delete account if contact creation fails\n            try:\n                await self.sdk.delete(\"accounts\", account_id)\n            except:\n                pass  # Ignore rollback errors\n            \n            raise e\n    \n    async def get_account_summary(self, account_id: str) -> dict:\n        \"\"\"Get comprehensive account summary.\"\"\"\n        \n        # Get account with related data\n        account = await self.sdk.read(\"accounts\", account_id, expand=[\n            \"primarycontactid($select=fullname,emailaddress1)\",\n            \"account_parent_account($select=name)\",\n            \"contact_customer_accounts($select=fullname,emailaddress1;$top=5)\"\n        ])\n        \n        # Get additional statistics\n        contact_count_result = await self.sdk.query(\"contacts\", {\n            \"filter\": f\"parentcustomerid eq '{account_id}'\",\n            \"count\": True,\n            \"top\": 0  # Just get count\n        })\n        \n        opportunity_count_result = await self.sdk.query(\"opportunities\", {\n            \"filter\": f\"customerid eq '{account_id}'\",\n            \"count\": True,\n            \"top\": 0\n        })\n        \n        return {\n            \"account\": account,\n            \"contact_count\": contact_count_result.total_count,\n            \"opportunity_count\": opportunity_count_result.total_count,\n            \"primary_contact\": account.get(\"primarycontactid\"),\n            \"parent_account\": account.get(\"account_parent_account\"),\n            \"recent_contacts\": account.get(\"contact_customer_accounts\", [])\n        }\n\n# Usage\nasync def main():\n    sdk = DataverseSDK(...)\n    \n    async with sdk:\n        manager = EntityManager(sdk)\n        \n        # Create account with contacts\n        result = await manager.create_account_with_contacts(\n            account_data={\"name\": \"Acme Corp\", \"websiteurl\": \"https://acme.com\"},\n            contacts_data=[\n                {\"firstname\": \"John\", \"lastname\": \"Doe\", \"emailaddress1\": \"john@acme.com\"},\n                {\"firstname\": \"Jane\", \"lastname\": \"Smith\", \"emailaddress1\": \"jane@acme.com\"}\n            ]\n        )\n        \n        # Get account summary\n        summary = await manager.get_account_summary(result[\"account_id\"])\n        print(f\"Account: {summary['account']['name']}\")\n        print(f\"Contacts: {summary['contact_count']}\")\n        print(f\"Opportunities: {summary['opportunity_count']}\")\n```\n\n## \ud83e\udd1d Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n### Development Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk.git\ncd crmadminbrasil-dataverse-sdk\n\n# Create virtual environment\npython -m venv venv\nsource venv/bin/activate  # On Windows: venv\\Scripts\\activate\n\n# Install development dependencies\npip install -e \".[dev]\"\n\n# Install pre-commit hooks\npre-commit install\n\n# Run tests\npytest\n\n# Run linting\nblack dataverse_sdk/\nisort dataverse_sdk/\nflake8 dataverse_sdk/\nmypy dataverse_sdk/\n```\n\n### Code Style\n\nWe use several tools to maintain code quality:\n\n- **Black**: Code formatting\n- **isort**: Import sorting\n- **flake8**: Linting\n- **mypy**: Type checking\n- **bandit**: Security analysis\n\n### Testing Guidelines\n\n- Write tests for all new features\n- Maintain test coverage above 90%\n- Include both unit and integration tests\n- Use descriptive test names and docstrings\n\n### Documentation\n\n- Update README.md for new features\n- Add docstrings to all public methods\n- Include type hints for all parameters and return values\n- Provide usage examples\n\n## \ud83d\udcc4 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## \ud83d\ude4f Acknowledgments\n\n- Microsoft Dataverse team for the excellent API\n- The Python async community for inspiration\n- All contributors who help improve this SDK\n\n## \ud83d\udcde Support\n\n- **Documentation**: [https://crmadminbrasil-dataverse-sdk.readthedocs.io](https://crmadminbrasil-dataverse-sdk.readthedocs.io)\n- **Issues**: [GitHub Issues](https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/crmadminbrasil-dataverse-sdk/crmadminbrasil-dataverse-sdk/discussions)\n- **Email**: support@crmadminbrasil-dataverse-sdk.com\n\n## \ud83d\uddfa\ufe0f Roadmap\n\n### Version 1.1\n- [ ] WebSocket support for real-time notifications\n- [ ] Enhanced FetchXML builder with GUI\n- [ ] Plugin system for custom entity types\n- [ ] Performance monitoring dashboard\n\n### Version 1.2\n- [ ] GraphQL-style query interface\n- [ ] Built-in data validation rules\n- [ ] Advanced caching strategies\n- [ ] Multi-tenant management tools\n\n### Version 2.0\n- [ ] Support for Dataverse for Teams\n- [ ] AI-powered query optimization\n- [ ] Visual query builder\n- [ ] Enterprise governance features\n\n---\n\n**Made with \u2764\ufe0f by the Dataverse SDK Team**\n\n\n\n## \u26a1 **Performance & Benchmarks**\n\nO SDK foi projetado para ser extremamente perform\u00e1tico, capaz de lidar com milh\u00f5es de registros em uso di\u00e1rio:\n\n### **Configura\u00e7\u00f5es Otimizadas**\n- **Batch Size**: > 100 registros por lote (padr\u00e3o: 500)\n- **Paralelismo**: At\u00e9 32 opera\u00e7\u00f5es simult\u00e2neas\n- **Pool de Conex\u00f5es**: 100 conex\u00f5es simult\u00e2neas\n- **Throughput**: > 1000 registros/segundo\n\n### **Testes de Performance**\n```bash\n# Executar benchmarks de performance\ncd benchmarks/\npip install -r requirements.txt\npython benchmark_bulk_create.py\n\n# Stress test com milh\u00f5es de registros\npython stress_test.py\n```\n\n### **Resultados T\u00edpicos**\n- \u2705 **Cria\u00e7\u00e3o em massa**: 1000+ registros/segundo\n- \u2705 **Consultas**: < 100ms para consultas simples\n- \u2705 **Bulk operations**: 10000+ registros/minuto\n- \u2705 **Mem\u00f3ria**: < 500MB para 100k registros\n\n## \ud83d\udcc1 **Estrutura do Projeto**\n\n```\ndataverse-sdk/\n\u251c\u2500\u2500 \ud83d\udce6 dataverse_sdk/          # C\u00f3digo principal do SDK\n\u251c\u2500\u2500 \ud83d\udda5\ufe0f cli/                    # Interface de linha de comando\n\u251c\u2500\u2500 \ud83e\uddea tests/                  # Testes unit\u00e1rios e integra\u00e7\u00e3o\n\u251c\u2500\u2500 \ud83d\udcda examples/               # Exemplos de uso\n\u251c\u2500\u2500 \u26a1 benchmarks/             # Testes de performance\n\u251c\u2500\u2500 \ud83d\udd27 scripts/                # Scripts utilit\u00e1rios\n\u251c\u2500\u2500 \ud83d\udcd6 docs/                   # Documenta\u00e7\u00e3o completa\n\u2502   \u251c\u2500\u2500 getting-started/       # Guias iniciais\n\u2502   \u251c\u2500\u2500 guides/                # Guias avan\u00e7ados\n\u2502   \u251c\u2500\u2500 tutorials/             # Tutoriais\n\u2502   \u251c\u2500\u2500 api-reference/         # Refer\u00eancia da API\n\u2502   \u251c\u2500\u2500 contributing/          # Guias de contribui\u00e7\u00e3o\n\u2502   \u251c\u2500\u2500 deployment/            # Guias de deployment\n\u2502   \u2514\u2500\u2500 jekyll/                # Site GitHub Pages\n\u2514\u2500\u2500 \ud83e\udd16 .github/               # Configura\u00e7\u00f5es GitHub\n```\n\n## \ud83d\udd17 **Links da Documenta\u00e7\u00e3o**\n\n- **[\ud83d\udcd6 Documenta\u00e7\u00e3o Completa](docs/)** - Toda a documenta\u00e7\u00e3o organizada\n- **[\ud83d\ude80 In\u00edcio R\u00e1pido](docs/getting-started/quickstart.md)** - Primeiros passos\n- **[\ud83c\udfe2 Configura\u00e7\u00e3o Corporativa](docs/deployment/CORPORATE_SETUP_GUIDE.md)** - Para ambientes empresariais\n- **[\u26a1 Benchmarks](benchmarks/)** - Testes de performance\n- **[\ud83e\udd1d Contribui\u00e7\u00e3o](docs/contributing/CONTRIBUTING.md)** - Como contribuir\n- **[\ud83d\udccb API Reference](docs/api-reference/dataverse-sdk.md)** - Documenta\u00e7\u00e3o t\u00e9cnica\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Async Python SDK for Microsoft Dataverse with enterprise features",
    "version": "1.1.4",
    "project_urls": {
        "Bug Tracker": "https://github.com/dataverse-sdk/dataverse-sdk/issues",
        "Changelog": "https://github.com/dataverse-sdk/dataverse-sdk/blob/main/CHANGELOG.md",
        "Documentation": "https://dataverse-sdk.readthedocs.io",
        "Homepage": "https://github.com/dataverse-sdk/dataverse-sdk",
        "Repository": "https://github.com/dataverse-sdk/dataverse-sdk"
    },
    "split_keywords": [
        "dataverse",
        " dynamics",
        " crm",
        " microsoft",
        " async",
        " sdk"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1bc930e60f1752a23a864157cc9f30bd68cded16d03ac111d2bc0b48dd4c7d58",
                "md5": "1cb7036784387ec55da8e1f41eaf3adf",
                "sha256": "702e6c66f786590459a6aa7d3aff4b4a6dcaa9e4e49324735c398593525353a8"
            },
            "downloads": -1,
            "filename": "crmadminbrasil_dataverse_sdk-1.1.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "1cb7036784387ec55da8e1f41eaf3adf",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 52992,
            "upload_time": "2025-07-15T20:48:00",
            "upload_time_iso_8601": "2025-07-15T20:48:00.490225Z",
            "url": "https://files.pythonhosted.org/packages/1b/c9/30e60f1752a23a864157cc9f30bd68cded16d03ac111d2bc0b48dd4c7d58/crmadminbrasil_dataverse_sdk-1.1.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "fb1ee21921c5df048d3348be01b47f787d9a937939238ff99a60976b0bfde4c6",
                "md5": "f73596bc6d2981632591debbb8da4420",
                "sha256": "558f74a780ed8a095a5a348552e43cc45f99b23ce83ff3854a10b9b23a482633"
            },
            "downloads": -1,
            "filename": "crmadminbrasil_dataverse_sdk-1.1.4.tar.gz",
            "has_sig": false,
            "md5_digest": "f73596bc6d2981632591debbb8da4420",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 74547,
            "upload_time": "2025-07-15T20:48:01",
            "upload_time_iso_8601": "2025-07-15T20:48:01.845156Z",
            "url": "https://files.pythonhosted.org/packages/fb/1e/e21921c5df048d3348be01b47f787d9a937939238ff99a60976b0bfde4c6/crmadminbrasil_dataverse_sdk-1.1.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-15 20:48:01",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dataverse-sdk",
    "github_project": "dataverse-sdk",
    "github_not_found": true,
    "lcname": "crmadminbrasil-dataverse-sdk"
}
        
Elapsed time: 0.78618s