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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.6.0] - 2026-07-16

### Added

- **Typed billing exceptions** in `foxnose_sdk.errors`, all subclasses of `FoxnoseAPIError`:
- `SpendCapExceeded` (HTTP 402 `spend_cap_reached`) — attrs `cap_usd`, `cycle_resets_at`, `raise_cap_url`
- `PlanExhausted` (HTTP 402 `plan_exhausted`) — attrs `axis`, `window_resets_at`, `upgrade_url`
- `PlanLimitExceeded` (HTTP 403 `plan_limit_exceeded`) — attrs `entity`, `limit`, `current`, `upgrade_url`
- `RateLimitExceeded` (HTTP 429 `rate_limited`) — attr `retry_after` (from the `Retry-After` header)
- All four are exported from the package root and caught by `except FoxnoseAPIError`.
- **Components on Collections** — `NestedFieldMeta` helper for building nested-field `meta` (`component`, `component_version`, `auto_update`), and `sync_collection_component()` on `ManagementClient` / `AsyncManagementClient` to advance pinned nested fields to a target Component version. New models `SyncComponentResponse`, `SyncComponentSkippedItem`, and `ComponentSyncConflictDetail`.

### Changed

- **Renamed Folder → Collection across the Management API surface.** New `*_collection*` methods (`list_collections`, `create_collection`, `list_collection_versions`, `list_collection_fields`, etc.) and models (`CollectionSummary`, `CollectionList`, `APICollectionSummary`, `APICollectionList`) are the preferred names. The old `*_folder*` methods and `FolderSummary` / `FolderList` remain as deprecated aliases that emit a one-shot `DeprecationWarning` on first use and will be removed in 1.0.

### Removed

- Composite folder support.

### Fixed

- `RolePermission.all_objects` is now optional (`bool | None`), so permissions for non-object-based content types — which the API returns with `all_objects: null` — parse without error.
- Corrected the `roles_and_permissions` example and the docs to the real permission wire-shape (`content_type` + `actions` list, with `all_objects` only on object-based content types), the renamed content-type keys (`collection-structure` / `collection-items` in place of `folder-*`), and the real API-key create shape (`description` + a single `role`).

## [0.5.0] - 2026-03-19

### Added

- **Vector search models** in `foxnose_sdk.flux.models`:
- `SearchMode` enum (`text`, `vector`, `vector_boosted`, `hybrid`)
- `VectorSearch` — auto-generated embedding search configuration
- `VectorFieldSearch` — custom pre-computed embedding search configuration
- `VectorBoostConfig` — boost configuration for `vector_boosted` mode
- `HybridConfig` — weight configuration for `hybrid` mode
- `SearchRequest` — typed search payload with cross-field validation
- **Convenience methods** on `FluxClient` and `AsyncFluxClient`:
- `vector_search()` — semantic search with auto-generated embeddings
- `vector_field_search()` — search with custom embedding vectors
- `hybrid_search()` — blended text + vector search
- `boosted_search()` — keyword search boosted by vector similarity
- **Vector Search documentation** — dedicated guide covering all search modes
- All convenience methods support `offset` and `**extra_body` pass-through for additional API parameters (`where`, `sort`, etc.)

### Fixed

- `examples/flux_client.py` search example now uses correct API keys (`find_text` and `results`)

## [0.4.2] - 2026-03-10

### Fixed

- **Secure Management/Flux signing with query parameters**:
- `SecureKeyAuth` now signs only the URL path (without query string)
- aligns SDK signatures with server-side verification and Management auth docs
- prevents `401 authentication_failed` / `Invalid signature` on requests with query params

## [0.4.1] - 2026-03-05

### Fixed

- **Flux role permission objects handling** in Management clients:
- normalize permission object list responses consistently
- keep compatibility with paginated/object payload variants
- align role-scoped flux permission object behavior with production contract

## [0.4.0] - 2026-02-25

### Added

- **Flux introspection methods** on sync and async clients:
- `get_router()` calls `GET /{api_prefix}/_router`
- `get_schema(folder_path)` calls `GET /{api_prefix}/{folder_path}/_schema`
- **API folder route description support** in Management clients:
- `add_api_folder()` and `update_api_folder()` now accept:
- `description_get_one`
- `description_get_many`
- `description_search`
- `description_schema`
- **`APIFolderSummary` model fields** for route descriptions:
- `description_get_one`
- `description_get_many`
- `description_search`
- `description_schema`

## [0.3.0] - 2026-02-10

### Added

- **`upsert_resource()`** method on `ManagementClient` and `AsyncManagementClient` — create or update a resource by `external_id` in a single call. Uses `PUT /folders/:folder/resources/?external_id=<value>`.
- **`batch_upsert_resources()`** method on `ManagementClient` and `AsyncManagementClient` — upsert multiple resources concurrently with configurable `max_concurrency`, `fail_fast` error handling mode, and optional `on_progress` callback.
- **`BatchUpsertItem`**, **`BatchItemError`**, **`BatchUpsertResult`** models for batch upsert input/output.
- **`external_id`** optional parameter on `create_resource()` — assign an external identifier when creating a resource via `POST`.
- **`external_id`** field on `ResourceSummary` model — populated in API responses for resources that have an external identifier.

## [0.2.0] - 2026-01-26

### Added

- **Model objects as identifiers** — Management client methods now accept either string keys or corresponding model objects (e.g. `FolderSummary`, `ResourceSummary`) wherever a `*_key` parameter is used. This eliminates the need to manually extract `.key` from objects returned by the API.
- `_resolve_key()` helper function for extracting string keys from model objects.
- 13 type aliases for method parameters: `FolderRef`, `ResourceRef`, `RevisionRef`, `ComponentRef`, `SchemaVersionRef`, `OrgRef`, `ProjectRef`, `EnvironmentRef`, `ManagementRoleRef`, `FluxRoleRef`, `ManagementAPIKeyRef`, `FluxAPIKeyRef`, `APIRef`.

## [0.1.0] - 2026-01-14

### Added

- Initial release of the FoxNose Python SDK
- `ManagementClient` for administrative operations
- `AsyncManagementClient` for async administrative operations
- `FluxClient` for content delivery
- `AsyncFluxClient` for async content delivery
- JWT authentication with automatic token refresh
- API key authentication for Flux API
- Comprehensive type hints and Pydantic models
- Automatic retry with exponential backoff
- Full support for all Management API endpoints:
- Organizations
- Projects
- Environments
- Folders
- Resources
- Revisions
- Schema versions and fields
- Components
- Locales
- Management roles and permissions
- Flux roles and permissions
- Management API keys
- Flux API keys

### Documentation

- Getting started guide
- Authentication guide
- Management Client reference
- Flux Client reference
- Error handling guide
- Code examples

[Unreleased]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.6.0...HEAD
[0.6.0]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.4.2...v0.5.0
[0.4.2]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.4.1...v0.4.2
[0.4.1]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/FoxNoseTech/foxnose-python/releases/tag/v0.1.0
104 changes: 99 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,20 @@ client = ManagementClient(
auth=JWTAuth.from_static_token("YOUR_ACCESS_TOKEN"),
)

# List folders
folders = client.list_folders()
for folder in folders.results:
print(f"{folder.name} ({folder.key})")
# List collections
collections = client.list_collections()
for collection in collections.results:
print(f"{collection.name} ({collection.key})")

client.close()
```

> **Note (0.6.0):** Folder-named methods (`list_folders`, `create_folder`, `add_api_folder`,
> `list_folder_versions`, `list_folder_fields`, etc.) remain as deprecated aliases that
> emit a one-shot `DeprecationWarning` on first use per process. They keep their
> original wire behaviour (hitting the legacy `/folders/...` URL alias on the server)
> and will be removed in **1.0**. Prefer the `*_collection*` names in new code.

### Async Client

```python
Expand All @@ -69,10 +75,98 @@ async def main():
auth=JWTAuth.from_static_token("YOUR_ACCESS_TOKEN"),
)

folders = await client.list_folders()
collections = await client.list_collections()
await client.aclose()
```

### Components on Collections

Collections can embed Components as nested fields with explicit pin
semantics (`component`, `component_version`, `auto_update`). The
`NestedFieldMeta` helper builds the `meta` block for you, and
`sync_collection_component` advances pinned fields to a target Component
version on demand.

```python
from foxnose_sdk import (
ManagementClient,
FoxnoseConfig,
NestedFieldMeta,
)
from foxnose_sdk.auth import JWTAuth

client = ManagementClient(
FoxnoseConfig(base_url="https://api.foxnose.com"),
environment_key="prod",
auth=JWTAuth("ACCESS_TOKEN"),
)

# Embed a Component as a pinned nested field on a Collection draft.
client.create_collection_field(
"articles",
"v2-draft",
{
"key": "seo",
"name": "SEO",
"type": "nested",
"required": True,
"meta": NestedFieldMeta(
component="cmp-seo-metadata",
component_version="ver-abc12345",
auto_update=False, # default — pin until explicit sync
).to_meta(),
},
)

# Later, advance every pinned nested field to its Component's current
# version (empty body = sync all pinned).
result = client.sync_collection_component("articles")
print(result.synced_paths, result.schema_version)

# Advance specific paths to a chosen Component version.
result = client.sync_collection_component(
"articles",
field_paths=["seo"],
to_versions={"seo": "ver-def67890"},
)
```

`sync_collection_component` returns a `SyncComponentResponse` with
`synced_paths`, `skipped` (per-path reasons), and `schema_version` (UID
of the newly published Collection schema version, or `None` if no field
needed advancing). On compatibility conflict the server returns 409
`component_sync_conflict`; quota exhaustion returns 422
`too_many_versions`. Both surface as `FoxnoseAPIError`.

### Handling billing errors

Billing and quota responses raise typed subclasses of `FoxnoseAPIError`, so
existing `except FoxnoseAPIError` handlers keep working while new code can read
the typed attributes:

```python
from foxnose_sdk import (
SpendCapExceeded,
PlanExhausted,
PlanLimitExceeded,
RateLimitExceeded,
)

try:
client.create_collection({"name": "Blog"})
except SpendCapExceeded as e: # HTTP 402
print(f"Spend cap {e.cap_usd}; resets at {e.cycle_resets_at}: {e.raise_cap_url}")
except PlanExhausted as e: # HTTP 402
print(f"Allowance for {e.axis} exhausted; resets at {e.window_resets_at}")
except PlanLimitExceeded as e: # HTTP 403
print(f"{e.entity}: {e.current}/{e.limit}. Upgrade: {e.upgrade_url}")
except RateLimitExceeded as e: # HTTP 429
print(f"Rate limited; retry after {e.retry_after}s")
```

All four subclass `FoxnoseAPIError`, so a single `except FoxnoseAPIError` still
catches them if you don't need the typed fields.

### Flux Client

```python
Expand Down
28 changes: 27 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.6.0] - 2026-07-16

### Added

- **Typed billing exceptions** in `foxnose_sdk.errors`, all subclasses of `FoxnoseAPIError`:
- `SpendCapExceeded` (HTTP 402 `spend_cap_reached`) — attrs `cap_usd`, `cycle_resets_at`, `raise_cap_url`
- `PlanExhausted` (HTTP 402 `plan_exhausted`) — attrs `axis`, `window_resets_at`, `upgrade_url`
- `PlanLimitExceeded` (HTTP 403 `plan_limit_exceeded`) — attrs `entity`, `limit`, `current`, `upgrade_url`
- `RateLimitExceeded` (HTTP 429 `rate_limited`) — attr `retry_after` (from the `Retry-After` header)
- All four are exported from the package root and caught by `except FoxnoseAPIError`.
- **Components on Collections** — `NestedFieldMeta` helper for building nested-field `meta` (`component`, `component_version`, `auto_update`), and `sync_collection_component()` on `ManagementClient` / `AsyncManagementClient` to advance pinned nested fields to a target Component version. New models `SyncComponentResponse`, `SyncComponentSkippedItem`, and `ComponentSyncConflictDetail`.

### Changed

- **Renamed Folder → Collection across the Management API surface.** New `*_collection*` methods (`list_collections`, `create_collection`, `list_collection_versions`, `list_collection_fields`, etc.) and models (`CollectionSummary`, `CollectionList`, `APICollectionSummary`, `APICollectionList`) are the preferred names. The old `*_folder*` methods and `FolderSummary` / `FolderList` remain as deprecated aliases that emit a one-shot `DeprecationWarning` on first use and will be removed in 1.0.

### Removed

- Composite folder support.

### Fixed

- `RolePermission.all_objects` is now optional (`bool | None`), so permissions for non-object-based content types — which the API returns with `all_objects: null` — parse without error.
- Corrected the `roles_and_permissions` example and the docs to the real permission wire-shape (`content_type` + `actions` list, with `all_objects` only on object-based content types), the renamed content-type keys (`collection-structure` / `collection-items` in place of `folder-*`), and the real API-key create shape (`description` + a single `role`).

## [0.5.0] - 2026-03-19

### Added
Expand Down Expand Up @@ -122,7 +147,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Error handling guide
- Code examples

[Unreleased]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.5.0...HEAD
[Unreleased]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.6.0...HEAD
[0.6.0]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.4.2...v0.5.0
[0.4.2]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.4.1...v0.4.2
[0.4.1]: https://github.com/FoxNoseTech/foxnose-python/compare/v0.4.0...v0.4.1
Expand Down
Loading
Loading