Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 28 additions & 18 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Technology Stack

- **Language**: Python 3.10+ (supports 3.10, 3.11, 3.12)
- **Language**: Python 3.10+ (supports 3.10, 3.11, 3.12, 3.13)
- **Build System**: Hatchling with uv-dynamic-versioning
- **Dependency Management**: uv (see uv.lock)
- **Core Dependencies**: ipsdk
Expand Down Expand Up @@ -52,6 +52,17 @@ uv run python -m build
uv sync --group dev
```

### Make Shortcuts
```bash
make test # Run test suite (verbose)
make coverage # Run tests with HTML coverage report
make lint # Lint src/ and tests/
make security # Run bandit security analysis
make premerge # Run full premerge checks locally
make tox # Test across Python 3.10–3.13
make clean # Remove build artifacts
```

## Architecture

### Core Components
Expand All @@ -72,24 +83,17 @@ uv sync --group dev

### Current Services

- **automation_studio** (`src/asyncplatform/services/automation_studio.py`): Manages Automation Studio projects and workflows
- `get_projects()`: Retrieve all projects with automatic pagination
- `describe_project()`: Get detailed project information
- `find_projects()`: Search projects by name
- `import_project()`: Import a project
- `delete_project()`: Delete a project by ID
- `patch_project()`: Update project fields
- `describe_workflow()`: Get workflow details

- **authorization** (`src/asyncplatform/services/authorization.py`): Manages authorization groups and accounts
- `get_groups()`: Retrieve all authorization groups with pagination
- `get_accounts()`: Retrieve all user accounts with pagination
- **automation_studio**: Manage projects and workflows (import, delete, patch, describe)
- **authorization**: Manage authorization groups and user accounts
- **configuration_manager**: Manage platform configuration
- **lifecycle_manager**: Manage platform lifecycle operations
- **operations_manager**: Manage platform operations
- **help**: Access platform help and documentation

### Current Resources

- **projects** (`src/asyncplatform/resources/projects.py`): High-level project management
- `importer()`: Import project with member assignments
- `delete()`: Delete project by name
- **projects**: Import projects with member assignments, delete by name
- **automations**: High-level automation management

### Service Base Class

Expand Down Expand Up @@ -136,11 +140,19 @@ async with asyncplatform.client(**cfg) as client:
- `src/asyncplatform/exceptions.py`: Custom exception classes
- `src/asyncplatform/jsonutils.py`: JSON utilities for API responses
- `src/asyncplatform/http.py`: HTTP enumerations and response wrapper
- `src/asyncplatform/heuristics.py`: Heuristic utilities
- `src/asyncplatform/metadata.py`: Package metadata
- `src/asyncplatform/models/`: Data models (e.g., ProjectMember)
- `examples/import_project.py`: Example usage patterns

## Development Notes

### Test Structure

- Tests live in `tests/unit/` (flat layout, not mirroring `src/`)
- Test files named `test_<module>.py` (e.g., `test_services_authorization.py`)
- `tox.ini` configures multi-version testing across Python 3.10–3.13

### Code Style and Type Annotations

- **Python 3.10+ Required**: All code uses modern Python 3.10+ features
Expand Down Expand Up @@ -209,5 +221,3 @@ async with asyncplatform.client(**cfg) as client:
- Use pathlib for file operations
- Prefer `pathlib.glob()` over manual iteration for file discovery
- Use dictionary comprehensions to filter None values from kwargs


134 changes: 36 additions & 98 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,169 +1,107 @@
# AsyncPlatform

An async Python client library for the Itential Platform REST API.
> Async Python client library for the Itential Platform REST API.

AsyncPlatform provides a high-level, asynchronous interface for interacting with Itential Automation Platform services. Built on top of [ipsdk](https://github.com/itential/ipsdk), it offers a plugin-based architecture with automatic service discovery and resource management.

## Features

- **Async/Await Support**: Built for modern Python with full asyncio support
- **High-Level Resources**: Complex operations simplified through resource abstractions
- **Type Hints**: Fully typed for better IDE support and type checking
- **Caching**: Built-in async-safe caching with TTL support
- **Context Management**: Automatic connection lifecycle management
AsyncPlatform provides a high-level, asynchronous interface for the Itential Automation Platform. It wraps [ipsdk](https://github.com/itential/ipsdk) with automatic service discovery, resource abstractions, and connection lifecycle management.

## Requirements

- Python 3.10 or higher
- Itential Platform 2023.1 or higher
- Python 3.10+
- Itential Platform 2023.1+
- [uv](https://github.com/astral-sh/uv) (recommended) or pip

## Installation

```bash
# with uv (recommended)
uv add asyncplatform

# with pip
pip install asyncplatform
```

## Quick Start

### Basic Usage

```python
import asyncio
import asyncplatform

async def main():
cfg = {
"host": "platform.example.com",
"user": "admin@domain",
"password": "your-password"
"password": "your-password",
}

async with asyncplatform.client(**cfg) as client:
# Access services directly
projects = await client.automation_studio.get_projects()
print(f"Found {len(projects)} projects")

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

### Using Services
## Usage

Services provide access to specific Itential Platform APIs:
### Services

Services map directly to Itential Platform APIs and are available as attributes on the client:

```python
async with asyncplatform.client(**cfg) as client:
# Get all projects
# Automation Studio
projects = await client.automation_studio.get_projects()

# Get specific project details
project = await client.automation_studio.describe_project("project-id")

# Get authorization groups
# Authorization
groups = await client.authorization.get_groups()

# Get user accounts
accounts = await client.authorization.get_accounts()
```

### Using Resources
Available services: `automation_studio`, `authorization`, `configuration_manager`,
`lifecycle_manager`, `operations_manager`, `help`.

### Resources

Resources provide high-level abstractions for complex operations:
Resources combine multiple service calls into single high-level operations:

```python
from asyncplatform.models.projects import ProjectMember

async with asyncplatform.client(**cfg) as client:
# Get the projects resource
projects = client.resource("projects")

# Import a project with member assignments
project_data = {
"name": "My Project",
"description": "Project description"
}

members = [
ProjectMember(name="admin_group", type="group", role="owner"),
ProjectMember(name="user@example.com", type="account", role="editor")
ProjectMember(name="user@example.com", type="account", role="editor"),
]

result = await projects.importer(project_data, members=members)
print(f"Imported project: {result['name']}")

# Delete a project by name
await projects.delete("My Project")
```

Available resources: `projects`, `automations`.

## Development

### Setup
**Prerequisites**: Python 3.10+, [uv](https://github.com/astral-sh/uv)

```bash
# Clone the repository
git clone https://github.com/itential/asyncplatform.git
cd asyncplatform

# Install dependencies
uv sync
uv sync --group dev
```

### Running Tests

```bash
# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=src/asyncplatform --cov-report=term

# Run specific test file
uv run pytest tests/unit/test_loader.py
make test # run tests
make lint # lint src/ and tests/
make coverage # test with HTML coverage report
make security # bandit security scan
make premerge # full premerge checks (lint + test + security)
make tox # test across Python 3.10–3.13
```

### Code Quality

```bash
# Linting
uv run ruff check src/asyncplatform tests

# Type checking
uv run mypy src/asyncplatform

# Formatting
uv run ruff format src/asyncplatform tests
```

## Contributing

Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, development workflow, and the process for submitting pull requests.

## Documentation

- [AGENTS.md](AGENTS.md) - Project overview and architecture for AI assistants
- [CONTRIBUTING.md](CONTRIBUTING.md) - Development guide and contribution guidelines
- [Itential Platform Documentation](https://docs.itential.com/)

## Support

- Report bugs and feature requests via [GitHub Issues](https://github.com/itential/asyncplatform/issues)
- For questions about the Itential Platform, visit [Itential Documentation](https://docs.itential.com/)
See [CONTRIBUTING.md](CONTRIBUTING.md) and the [`docs/`](docs/) directory for architecture, testing patterns, and contribution guidelines.

## License

Copyright (c) 2025 Itential, Inc

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

See [LICENSE](LICENSE) for the full license text and [LICENSES.md](LICENSES.md) for third-party license information.
Copyright (c) 2025 Itential, Inc. GPL-3.0-or-later — see [LICENSE](LICENSE) and [NOTICE](NOTICE).
32 changes: 15 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Typing :: Typed",
]

Expand All @@ -41,15 +42,14 @@ email = "opensource@itential.com"
[project.urls]
Homepage = "https://itential.com"
Repository = "https://github.com/itential/asyncplatform"
Documentation = "https://github.com/itential/asyncplatform"
Documentation = "https://github.com/itential/asyncplatform/tree/main/docs"


[dependency-groups]
dev = [
"pytest",
"pytest-cov",
"pytest-asyncio",
"q",
"ruff",
"mypy",
"coverage",
Expand All @@ -64,9 +64,6 @@ dev = [
requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"]
build-backend = "hatchling.build"

[tool.setuptools.dynamic]
version = { attr = "asyncplatform.metadata.version" }

[tool.hatch.version]
source = "uv-dynamic-versioning"

Expand Down Expand Up @@ -199,8 +196,6 @@ ignore = [
"S105", "S106", "S107",
# Ignore complexity
"C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915",
# Allow print statements (useful for debugging)
"T201",
# Allow assert statements (used for internal validation in SDK)
"S101",
# Allow subprocess without shell=False (we control the input)
Expand All @@ -209,20 +204,10 @@ ignore = [
"S607",
# Allow use of `typing.Any`
"ANN401",
# Allow missing docstrings for magic methods
"D105",
# Allow missing docstrings in __init__
"D107",
# Allow relative imports for local packages
"TID252",
# Allow catching broad exceptions (necessary for HTTP client)
"BLE001",
# Allow raise without from inside except
"B904",
# Allow too many return statements
"PLR0911",
# Allow too many arguments
"PLR0913",
# Allow implicit string concatenation (useful for long strings)
"ISC001",
# Allow passing strings directly to exceptions
Expand Down Expand Up @@ -288,6 +273,7 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
"G004", # Logging statement uses f-string (acceptable for informational logging)
"E501", # Line too long (docstrings can be longer)
"PLW0603", # Global statement (used for module-level state)
"T201", # print used intentionally in fatal() to write to stderr before sys.exit
]

"src/asyncplatform/heuristics.py" = [
Expand Down Expand Up @@ -385,3 +371,15 @@ disable_error_code = ["attr-defined", "arg-type"]
module = "asyncplatform.resources.*"
# Ignore attribute errors for dynamically loaded service attributes
disable_error_code = ["attr-defined"]

[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "strict"

[tool.coverage.run]
source = ["src/asyncplatform"]
omit = ["tests/*"]

[tool.coverage.report]
show_missing = true
skip_covered = false
2 changes: 1 addition & 1 deletion src/asyncplatform/heuristics.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def add_pattern(
)
except re.error as e:
msg = f"Invalid regex pattern for '{name}': {e}"
raise re.error(msg)
raise re.error(msg) from e

def remove_pattern(self, name: str) -> bool:
"""Remove a pattern from the scanner.
Expand Down
2 changes: 1 addition & 1 deletion src/asyncplatform/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def json(self) -> dict[str, Any]:
return self._response.json()
except Exception as exc:
msg = f"Failed to parse response as JSON: {exc!s}"
raise ValueError(msg)
raise ValueError(msg) from exc

def raise_for_status(self) -> None:
"""
Expand Down
Loading
Loading