Source code for pinecone.admin.api_keys

"""ApiKeys namespace — list, create, describe, update, and delete operations."""

from __future__ import annotations

import builtins
import logging
from typing import TYPE_CHECKING, Any

from pinecone._internal.adapters.admin_adapter import AdminAdapter
from pinecone._internal.validation import require_non_empty
from pinecone.errors.exceptions import ValidationError
from pinecone.models.admin.api_key import APIKeyList, APIKeyModel, APIKeyRole, APIKeyWithSecret

if TYPE_CHECKING:
    from pinecone._internal.http_client import HTTPClient

logger = logging.getLogger(__name__)

_VALID_ROLES = {r.value for r in APIKeyRole}


def _validate_roles(roles: builtins.list[APIKeyRole | str]) -> builtins.list[APIKeyRole]:
    """Validate each role and return typed enum values."""
    result: builtins.list[APIKeyRole] = []
    for role in roles:
        role_str = role.value if isinstance(role, APIKeyRole) else role
        if role_str not in _VALID_ROLES:
            opts = ", ".join(repr(v) for v in sorted(_VALID_ROLES))
            raise ValidationError(f"Invalid role {role_str!r}. Must be one of {opts}")
        result.append(APIKeyRole(role_str))
    return result


[docs] class ApiKeys: """Control-plane operations for Pinecone API keys. Provides methods to list, create, describe, update, and delete API keys scoped to a project. Args: http (HTTPClient): HTTP client for making API requests. Examples: >>> from pinecone import Admin >>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret") >>> for key in admin.api_keys.list(project_id="proj-abc123"): ... print(key.name) """
[docs] def __init__(self, *, http: HTTPClient) -> None: self._http = http self._adapter = AdminAdapter()
def __repr__(self) -> str: """Return developer-friendly representation.""" return "ApiKeys()"
[docs] def list(self, *, project_id: str) -> APIKeyList: """List all API keys for a project. Args: project_id (str): The identifier of the project. Returns: An :class:`APIKeyList` supporting iteration, len(), and index access. Raises: :exc:`~pinecone.errors.exceptions.PineconeValueError`: If *project_id* is empty. :exc:`ApiError`: If the API returns an error response. Examples: >>> from pinecone import Admin >>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret") >>> for key in admin.api_keys.list(project_id="proj-abc123"): ... print(key.name) """ require_non_empty("project_id", project_id) logger.info("Listing API keys for project %r", project_id) response = self._http.get(f"/admin/projects/{project_id}/api-keys") result = self._adapter.to_api_key_list(response.content) logger.debug("Listed %d API keys", len(result)) return result
[docs] def create( self, *, project_id: str, name: str, description: str | None = None, roles: builtins.list[APIKeyRole | str] | None = None, ) -> APIKeyWithSecret: """Create a new API key for a project. Args: project_id (str): The identifier of the project. name (str): Name for the new API key (1-80 characters). description (str | None): Optional description for the API key. roles (list[APIKeyRole | str] | None): Roles to assign to the key. Valid values are ``"ProjectEditor"``, ``"ProjectViewer"``, ``"ControlPlaneEditor"``, ``"ControlPlaneViewer"``, ``"DataPlaneEditor"``, and ``"DataPlaneViewer"``. Defaults to ``["ProjectEditor"]`` if omitted. Returns: An :class:`APIKeyWithSecret` containing the key metadata and secret value. The secret value is only available at creation time. Raises: :exc:`~pinecone.errors.exceptions.PineconeValueError`: If *project_id* or *name* is empty. :exc:`ApiError`: If the API returns an error response. Examples: >>> from pinecone import Admin, APIKeyRole >>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret") >>> result = admin.api_keys.create( ... project_id="proj-abc123", name="prod-search-key", ... roles=[APIKeyRole.PROJECT_EDITOR] ... ) >>> result.value 'pcsk_abc123_secretvalue' >>> result = admin.api_keys.create( ... project_id="proj-abc123", name="ci-pipeline-key", roles=["ProjectViewer"] ... ) >>> result.key.roles # doctest: +SKIP ['ProjectViewer'] """ require_non_empty("project_id", project_id) require_non_empty("name", name) body: dict[str, Any] = {"name": name} if description is not None: body["description"] = description if roles is not None: body["roles"] = _validate_roles(roles) logger.info("Creating API key %r in project %r", name, project_id) response = self._http.post(f"/admin/projects/{project_id}/api-keys", json=body) result = self._adapter.to_api_key_with_secret(response.content) logger.debug("Created API key %r", result.key.id) return result
[docs] def describe(self, *, api_key_id: str) -> APIKeyModel: """Get detailed information about an API key. Args: api_key_id (str): The identifier of the API key. Returns: An :class:`APIKeyModel` with full API key details. Raises: :exc:`~pinecone.errors.exceptions.PineconeValueError`: If *api_key_id* is empty. :exc:`ApiError`: If the API returns an error response. Examples: >>> from pinecone import Admin >>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret") >>> key = admin.api_keys.describe(api_key_id="key-abc123") >>> key.name 'prod-search-key' """ require_non_empty("api_key_id", api_key_id) logger.info("Describing API key %r", api_key_id) response = self._http.get(f"/admin/api-keys/{api_key_id}") result = self._adapter.to_api_key(response.content) logger.debug("Described API key %r", api_key_id) return result
[docs] def update( self, *, api_key_id: str, name: str | None = None, roles: builtins.list[APIKeyRole | str] | None = None, ) -> APIKeyModel: """Update an API key's settings. When *roles* is provided, it replaces the entire role set. Args: api_key_id (str): The identifier of the API key to update. name (str | None): New name for the API key. roles (list[APIKeyRole | str] | None): New roles for the API key. Replaces all existing roles. Returns: An :class:`APIKeyModel` with the updated API key details. Raises: :exc:`~pinecone.errors.exceptions.PineconeValueError`: If *api_key_id* is empty. :exc:`ApiError`: If the API returns an error response. Examples: >>> from pinecone import Admin >>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret") >>> key = admin.api_keys.update( ... api_key_id="key-abc123", name="new-name" ... ) >>> key.name # doctest: +SKIP 'new-name' """ require_non_empty("api_key_id", api_key_id) body: dict[str, Any] = {} if name is not None: body["name"] = name if roles is not None: body["roles"] = _validate_roles(roles) logger.info("Updating API key %r", api_key_id) response = self._http.patch(f"/admin/api-keys/{api_key_id}", json=body) result = self._adapter.to_api_key(response.content) logger.debug("Updated API key %r", api_key_id) return result
[docs] def delete(self, *, api_key_id: str) -> None: """Delete an API key. Args: api_key_id (str): The identifier of the API key to delete. Raises: :exc:`~pinecone.errors.exceptions.PineconeValueError`: If *api_key_id* is empty. :exc:`ApiError`: If the API returns an error response. Examples: >>> from pinecone import Admin >>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret") >>> admin.api_keys.delete(api_key_id="key-abc123") """ require_non_empty("api_key_id", api_key_id) logger.info("Deleting API key %r", api_key_id) self._http.delete(f"/admin/api-keys/{api_key_id}") logger.debug("Deleted API key %r", api_key_id)