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
33 changes: 33 additions & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,39 @@ Export data to a database.
**validate_api_key()**
Test API key authentication.

### Set Management

A "set" is a persistent, named snapshot of rows (e.g. a regression dataset or a
shared fixture). Sets are scoped to a project/team and appear on the DataMaker
Sets page.

**get_sets(project_id=None)**
Get all saved sets. Pass `project_id` (or set `DATAMAKER_PROJECT_ID`) to scope
the listing to one project.

**get_set(set_id)**
Get a single set by ID, including its full saved rows in `data`.

**create_set(name, data=None, description=None, row_count=None, project_id=None)**
Create (save) a new set. `data` is typically a list of row dicts.

**update_set(set_id, name=None, description=None, data=None, row_count=None)**
Update a saved set. Only the provided fields are changed.

**delete_set(set_id)**
Delete a saved set by ID.

**save_set(name, data, description=None, project_id=None)**
Convenience method to save rows as a named set. Pulls project context from
`DATAMAKER_PROJECT_ID` when not provided.

```python
dm = DataMaker()
rows = dm.generate(template)
result = dm.save_set("nightly-regression", rows)
print(f"Saved set: {result['name']} ({result['rowCount']} rows)")
```

## Field Types Reference

### Basic Types
Expand Down
132 changes: 132 additions & 0 deletions src/datamaker/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from .routes.export_and_validation import ExportClient, ValidationClient
from .routes.scenario_files import ScenarioFilesClient
from .routes.sets import SetsClient

load_dotenv()

Expand Down Expand Up @@ -68,6 +69,7 @@ def __init__(
self._scenario_files = ScenarioFilesClient(
api_key, default_headers, base_url, verify
)
self._sets = SetsClient(api_key, default_headers, base_url, verify)

# Maintain backward compatibility
self.api_key = self._generation.api_key
Expand Down Expand Up @@ -661,6 +663,131 @@ def save_file(
folder=folder,
)

# =================== SET METHODS ===================
def get_sets(self, project_id: Optional[str] = None):
"""Get all saved sets for the caller's project/team scope.

Args:
project_id: Optional project ID to scope the listing to. Falls back
to the DATAMAKER_PROJECT_ID env var.

Returns:
A list of set metadata dictionaries.
"""
return self._sets.get_sets(project_id)

def get_set(self, set_id: str):
"""Get a single saved set by ID, including its full rows payload.

Args:
set_id: The unique identifier of the set.

Returns:
The set dictionary (including its saved rows in ``data``).
"""
return self._sets.get_set(set_id)

def create_set(
self,
name: str,
data=None,
description: Optional[str] = None,
row_count: Optional[int] = None,
project_id: Optional[str] = None,
):
"""Create (save) a new set.

Args:
name: A name for the saved set.
data: The rows payload to save - typically a list of row dicts.
description: Optional short description of what the set captures.
row_count: Optional explicit row count (derived from data otherwise).
project_id: Optional project ID. Falls back to DATAMAKER_PROJECT_ID.

Returns:
The created set dictionary.
"""
return self._sets.create_set(
name=name,
data=data,
description=description,
row_count=row_count,
project_id=project_id,
)

def update_set(
self,
set_id: str,
name: Optional[str] = None,
description: Optional[str] = None,
data=None,
row_count: Optional[int] = None,
):
"""Update a saved set.

Args:
set_id: The unique identifier of the set to update.
name: New name for the set.
description: New description.
data: Replacement rows payload.
row_count: Explicit row count override.

Returns:
The updated set dictionary.
"""
return self._sets.update_set(
set_id,
name=name,
description=description,
data=data,
row_count=row_count,
)

def delete_set(self, set_id: str):
"""Delete a saved set by ID.

Args:
set_id: The unique identifier of the set to delete.

Returns:
Confirmation response.
"""
return self._sets.delete_set(set_id)

def save_set(
self,
name: str,
data,
description: Optional[str] = None,
project_id: Optional[str] = None,
):
"""Convenience method to save rows as a named set.

Project context is pulled from DATAMAKER_PROJECT_ID when not provided,
which is convenient inside DataMaker sandbox environments.

Args:
name: A name for the saved set.
data: The rows to save - typically a list of row dicts.
description: Optional short description of what the set captures.
project_id: Optional project ID. Falls back to DATAMAKER_PROJECT_ID.

Returns:
The created set dictionary.

Example:
>>> dm = DataMaker()
>>> rows = dm.generate(template)
>>> result = dm.save_set("nightly-regression", rows)
>>> print(f"Saved set: {result['name']} ({result['rowCount']} rows)")
"""
return self._sets.save_set(
name=name,
data=data,
description=description,
project_id=project_id,
)

# =================== PROPERTY ACCESS TO CLIENTS ===================
# For advanced users who want direct access to specific clients
@property
Expand Down Expand Up @@ -747,3 +874,8 @@ def validation(self):
def scenario_files(self):
"""Access to scenario files client."""
return self._scenario_files

@property
def sets(self):
"""Access to sets client."""
return self._sets
2 changes: 2 additions & 0 deletions src/datamaker/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .folders_and_utils import TemplateFoldersClient, ShortcutsClient, FeedbackClient
from .export_and_validation import ExportClient, ValidationClient
from .scenario_files import ScenarioFilesClient
from .sets import SetsClient

__all__ = [
"BaseClient",
Expand All @@ -32,4 +33,5 @@
"ExportClient",
"ValidationClient",
"ScenarioFilesClient",
"SetsClient",
]
177 changes: 177 additions & 0 deletions src/datamaker/routes/sets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
"""Client for set operations - persistent named snapshots of rows.

A "set" is a saved snapshot of generated or fetched rows (e.g. a pinned
regression dataset, a shared fixture, or an audit copy of what was masked and
exported). Sets are scoped to a project/team and show up on the DataMaker Sets
page.
"""

import os
from typing import Dict, List, Optional, Any
from .base import BaseClient
from ..error import DataMakerError


class SetsClient(BaseClient):
"""Client for set operations (persistent saved row snapshots)."""

def get_sets(self, project_id: Optional[str] = None) -> List[Dict]:
"""Fetch all saved sets for the caller's project/team scope.

Args:
project_id: Optional project ID to scope the listing to. Falls back
to the DATAMAKER_PROJECT_ID env var. When omitted, the API
returns every set the API key can access.

Returns:
A list of set metadata dictionaries.
"""
project_id = project_id or os.environ.get("DATAMAKER_PROJECT_ID")

endpoint = "/sets"
if project_id:
endpoint += f"?projectId={project_id}"

response = self._make_request("GET", endpoint)
return response.json()

def get_set(self, set_id: str) -> Dict:
"""Get a single saved set by ID, including its full ``data`` payload.

Args:
set_id: The unique identifier of the set.

Returns:
The set dictionary (including its saved rows in ``data``).
"""
response = self._make_request("GET", f"/sets/{set_id}")
return response.json()

def create_set(
self,
name: str,
data: Optional[Any] = None,
description: Optional[str] = None,
row_count: Optional[int] = None,
project_id: Optional[str] = None,
) -> Dict:
"""Create (save) a new set.

Args:
name: A name for the saved set.
data: The rows payload to save - typically a list of row dicts.
When omitted, an empty set is created.
description: Optional short description of what the set captures.
row_count: Optional explicit row count. Derived from ``data`` by the
API when omitted.
project_id: Optional project ID the set belongs to. Falls back to the
DATAMAKER_PROJECT_ID env var. Required when the API key is not
already scoped to a single project.

Returns:
The created set dictionary.

Raises:
DataMakerError: If ``name`` is not provided.
"""
if not name:
raise DataMakerError("name is required to create a set.")

project_id = project_id or os.environ.get("DATAMAKER_PROJECT_ID")

set_data: Dict[str, Any] = {"name": name}
if data is not None:
set_data["data"] = data
if description is not None:
set_data["description"] = description
if row_count is not None:
set_data["rowCount"] = row_count
if project_id:
set_data["projectId"] = project_id

response = self._make_request("POST", "/sets", json=set_data)
return response.json()

def update_set(
self,
set_id: str,
name: Optional[str] = None,
description: Optional[str] = None,
data: Optional[Any] = None,
row_count: Optional[int] = None,
) -> Dict:
"""Update a saved set.

Only the fields that are provided are sent; the rest are left unchanged.

Args:
set_id: The unique identifier of the set to update.
name: New name for the set.
description: New description (pass ``None`` to leave unchanged).
data: Replacement rows payload. The API keeps ``rowCount`` in sync
whenever ``data`` changes.
row_count: Explicit row count override.

Returns:
The updated set dictionary.
"""
update_data: Dict[str, Any] = {}
if name is not None:
update_data["name"] = name
if description is not None:
update_data["description"] = description
if data is not None:
update_data["data"] = data
if row_count is not None:
update_data["rowCount"] = row_count

response = self._make_request("PATCH", f"/sets/{set_id}", json=update_data)
return response.json()

def delete_set(self, set_id: str) -> Dict:
"""Delete a saved set by ID.

Args:
set_id: The unique identifier of the set to delete.

Returns:
Confirmation response.
"""
response = self._make_request("DELETE", f"/sets/{set_id}")
return response.json()

def save_set(
self,
name: str,
data: Any,
description: Optional[str] = None,
project_id: Optional[str] = None,
) -> Dict:
"""Convenience method to save rows as a named set.

This is the most common entry point: hand it a name and the rows you
want to keep. Project context is pulled from the DATAMAKER_PROJECT_ID
env var when not provided, which is convenient inside DataMaker sandbox
environments where it is pre-configured.

Args:
name: A name for the saved set.
data: The rows to save - typically a list of row dicts.
description: Optional short description of what the set captures.
project_id: Optional project ID. Falls back to DATAMAKER_PROJECT_ID.

Returns:
The created set dictionary.

Example:
>>> dm = DataMaker()
>>> rows = dm.generate(template)
>>> result = dm.save_set("nightly-regression", rows)
>>> print(f"Saved set: {result['name']} ({result['rowCount']} rows)")
"""
return self.create_set(
name=name,
data=data,
description=description,
project_id=project_id,
)
Loading
Loading