"""Organizations namespace — list, describe, update, and delete operations."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from urllib.parse import quote
from pinecone._internal.adapters.admin_adapter import AdminAdapter
from pinecone._internal.validation import require_non_empty
from pinecone.models.admin.organization import OrganizationList, OrganizationModel
if TYPE_CHECKING:
from pinecone._internal.http_client import HTTPClient
logger = logging.getLogger(__name__)
[docs]
class Organizations:
"""Operations on Pinecone organizations.
An organization is the top-level account boundary in Pinecone: it holds projects,
users, and billing, and everything else an :class:`~pinecone.Admin` client touches
lives inside one. Where a project scopes indexes and API keys, an organization scopes
projects, members, and the bill. Not constructed directly — reach it as
``admin.organizations``.
Examples:
>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for org in admin.organizations.list():
... print(org.name)
.. seealso::
:class:`~pinecone.admin.projects.Projects` — the projects inside an organization.
"""
[docs]
def __init__(self, *, http: HTTPClient) -> None:
self._http = http
self._adapter = AdminAdapter()
def __repr__(self) -> str:
"""Return developer-friendly representation."""
return "Organizations()"
[docs]
def list(self) -> OrganizationList:
"""List the organizations your credentials can reach.
Returns:
An :class:`OrganizationList` of every reachable organization, supporting
iteration, ``len()``, and index access. Returned whole — there is no paging.
Examples:
>>> for org in admin.organizations.list():
... print(org.name, org.plan)
"""
logger.info("Listing organizations")
response = self._http.get("/admin/organizations")
result = self._adapter.to_organization_list(response.content)
logger.debug("Listed %d organizations", len(result))
return result
[docs]
def describe(self, *, organization_id: str) -> OrganizationModel:
"""Get details for one organization.
Args:
organization_id (str): The organization's identifier, e.g. ``"org-abc123"``.
Returns:
An :class:`OrganizationModel` with the organization's name, plan,
payment status, support tier, and creation time.
Raises:
:exc:`~pinecone.errors.exceptions.PineconeValueError`: If *organization_id* is
empty or whitespace-only. Checked before the request is sent.
Examples:
>>> org = admin.organizations.describe(organization_id="org-abc123")
>>> org.name
'Acme Corp'
"""
require_non_empty("organization_id", organization_id)
logger.info("Describing organization %r", organization_id)
response = self._http.get(f"/admin/organizations/{quote(organization_id, safe='')}")
result = self._adapter.to_organization(response.content)
logger.debug("Described organization %r", organization_id)
return result
[docs]
def update(self, *, organization_id: str, name: str) -> OrganizationModel:
"""Rename an organization.
The name is the only organization field this SDK can change; plan, payment status,
and support tier are read-only here.
Args:
organization_id (str): The organization's identifier, e.g. ``"org-abc123"``.
name (str): The new display name, e.g. ``"Acme Corporation"``. Unlike
*organization_id*, it is not checked client-side.
Returns:
An :class:`OrganizationModel` carrying the name as it was stored.
Raises:
:exc:`~pinecone.errors.exceptions.PineconeValueError`: If *organization_id* is
empty or whitespace-only. Checked before the request is sent.
Examples:
>>> org = admin.organizations.update(
... organization_id="org-abc123", name="Acme Corporation"
... )
"""
require_non_empty("organization_id", organization_id)
logger.info("Updating organization %r", organization_id)
response = self._http.patch(
f"/admin/organizations/{quote(organization_id, safe='')}",
json={"name": name},
)
result = self._adapter.to_organization(response.content)
logger.debug("Updated organization %r", organization_id)
return result
[docs]
def delete(self, *, organization_id: str) -> None:
"""Delete an organization permanently.
There is no undo and no soft-delete window. An organization must meet three
conditions before it can be deleted:
- It is not on a paid plan (downgrade first).
- Its payment status is active, with no open invoices.
- It contains no projects (see
:meth:`Projects.delete <pinecone.admin.projects.Projects.delete>`).
All three must hold at once — an organization with no projects can
still be blocked by its plan or payment status.
Args:
organization_id (str): The organization's identifier, e.g. ``"org-abc123"``.
Raises:
:exc:`~pinecone.errors.exceptions.PineconeValueError`: If *organization_id* is
empty or whitespace-only. Checked before the request is sent.
:exc:`~pinecone.errors.exceptions.FailedPreconditionError`: If the
organization is on a paid plan, its payment status is not active, or it
still contains projects. The error message names the blocker.
Examples:
>>> admin.organizations.delete(organization_id="org-abc123")
"""
require_non_empty("organization_id", organization_id)
logger.info("Deleting organization %r", organization_id)
self._http.delete(f"/admin/organizations/{quote(organization_id, safe='')}")
logger.debug("Deleted organization %r", organization_id)