Skip to content

Commit

Permalink
Merge pull request #405 from mesozoic/enterprise_new_params
Browse files Browse the repository at this point in the history
Fix enterprise.info and enterprise.users after new API params
  • Loading branch information
mesozoic authored Nov 12, 2024
2 parents 530ce53 + 067086f commit 9bf990a
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 24 deletions.
57 changes: 52 additions & 5 deletions pyairtable/api/enterprise.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,26 @@ def __init__(self, api: "pyairtable.api.api.Api", workspace_id: str):
self._info: Optional[EnterpriseInfo] = None

@cache_unless_forced
def info(self) -> EnterpriseInfo:
def info(
self,
*,
aggregated: bool = False,
descendants: bool = False,
) -> EnterpriseInfo:
"""
Retrieve basic information about the enterprise, caching the result.
Calls `Get enterprise <https://airtable.com/developers/web/api/get-enterprise>`__.
Args:
aggregated: if ``True``, include aggregated values across the enterprise.
descendants: if ``True``, include information about the enterprise's descendant orgs.
"""
params = {"include": ["collaborators", "inviteLinks"]}
include = []
if aggregated:
include.append("aggregated")
if descendants:
include.append("descendants")
params = {"include": include}
response = self.api.get(self.urls.meta, params=params)
return EnterpriseInfo.from_api(response, self.api)

Expand All @@ -102,21 +117,41 @@ def group(self, group_id: str, collaborations: bool = True) -> UserGroup:
payload = self.api.get(self.urls.group(group_id), params=params)
return UserGroup.model_validate(payload)

def user(self, id_or_email: str, collaborations: bool = True) -> UserInfo:
def user(
self,
id_or_email: str,
*,
collaborations: bool = True,
aggregated: bool = False,
descendants: bool = False,
) -> UserInfo:
"""
Retrieve information on a single user with the given ID or email.
Args:
id_or_email: A user ID (``usrQBq2RGdihxl3vU``) or email address.
collaborations: If ``False``, no collaboration data will be requested
from Airtable. This may result in faster responses.
aggregated: If ``True``, includes the user's aggregated values
across this enterprise account and its descendants.
descendants: If ``True``, includes information about the user
in a ``dict`` keyed per descendant enterprise account.
"""
return self.users([id_or_email], collaborations=collaborations)[0]
users = self.users(
[id_or_email],
collaborations=collaborations,
aggregated=aggregated,
descendants=descendants,
)
return users[0]

def users(
self,
ids_or_emails: Iterable[str],
*,
collaborations: bool = True,
aggregated: bool = False,
descendants: bool = False,
) -> List[UserInfo]:
"""
Retrieve information on the users with the given IDs or emails.
Expand All @@ -128,18 +163,30 @@ def users(
or email addresses (or both).
collaborations: If ``False``, no collaboration data will be requested
from Airtable. This may result in faster responses.
aggregated: If ``True``, includes the user's aggregated values
across this enterprise account and its descendants.
descendants: If ``True``, includes information about the user
in a ``dict`` keyed per descendant enterprise account.
"""
user_ids: List[str] = []
emails: List[str] = []
for value in ids_or_emails:
(emails if "@" in value else user_ids).append(value)

include = []
if collaborations:
include.append("collaborations")
if aggregated:
include.append("aggregated")
if descendants:
include.append("descendants")

response = self.api.get(
url=self.urls.users,
params={
"id": user_ids,
"email": emails,
"include": ["collaborations"] if collaborations else [],
"include": include,
},
)
# key by user ID to avoid returning duplicates
Expand Down
22 changes: 22 additions & 0 deletions pyairtable/models/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,11 +440,18 @@ class EnterpriseInfo(AirtableModel):
email_domains: List["EnterpriseInfo.EmailDomain"]
root_enterprise_id: str = pydantic.Field(alias="rootEnterpriseAccountId")
descendant_enterprise_ids: List[str] = _FL(alias="descendantEnterpriseAccountIds")
aggregated: Optional["EnterpriseInfo.AggregatedIds"] = None
descendants: Dict[str, "EnterpriseInfo.AggregatedIds"] = _FD()

class EmailDomain(AirtableModel):
email_domain: str
is_sso_required: bool

class AggregatedIds(AirtableModel):
group_ids: List[str] = _FL()
user_ids: List[str] = _FL()
workspace_ids: List[str] = _FL()


class WorkspaceCollaborators(_Collaborators, url="meta/workspaces/{self.id}"):
"""
Expand Down Expand Up @@ -577,10 +584,25 @@ class UserInfo(
is_super_admin: bool = False
groups: List[NestedId] = _FL()
collaborations: "Collaborations" = _F("Collaborations")
descendants: Dict[str, "UserInfo.DescendantIds"] = _FD()
aggregated: Optional["UserInfo.AggregatedIds"] = None

def logout(self) -> None:
self._api.post(self._url + "/logout")

class DescendantIds(AirtableModel):
last_activity_time: Optional[datetime] = None
collaborations: Optional["Collaborations"] = None
is_admin: bool = False
is_managed: bool = False
groups: List[NestedId] = _FL()

class AggregatedIds(AirtableModel):
last_activity_time: Optional[datetime] = None
collaborations: Optional["Collaborations"] = None
is_admin: bool = False
groups: List[NestedId] = _FL()


class UserGroup(AirtableModel):
"""
Expand Down
4 changes: 2 additions & 2 deletions pyairtable/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,9 @@ def cache_unless_forced(func: Callable[[C], R]) -> _FetchMethod[C, R]:
attr = "_cached_" + attr.lstrip("_")

@wraps(func)
def _inner(self: C, *, force: bool = False) -> R:
def _inner(self: C, *, force: bool = False, **kwargs: Any) -> R:
if force or getattr(self, attr, None) is None:
setattr(self, attr, func(self))
setattr(self, attr, func(self, **kwargs))
return cast(R, getattr(self, attr))

_inner.__annotations__["force"] = bool
Expand Down
32 changes: 19 additions & 13 deletions scripts/find_model_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"pyairtable.models.schema:BaseCollaborators": "operations:get-base-collaborators:response:schema",
"pyairtable.models.schema:BaseCollaborators.IndividualCollaborators": "operations:get-base-collaborators:response:schema:@individualCollaborators",
"pyairtable.models.schema:BaseCollaborators.GroupCollaborators": "operations:get-base-collaborators:response:schema:@groupCollaborators",
"pyairtable.models.schema:BaseCollaborators.InterfaceCollaborators": "operations:get-base-collaborators:response:schema:@interfaces:additionalProperties",
"pyairtable.models.schema:BaseCollaborators.InterfaceCollaborators": "operations:get-base-collaborators:response:schema:@interfaces:*",
"pyairtable.models.schema:BaseCollaborators.InviteLinks": "operations:get-base-collaborators:response:schema:@inviteLinks",
"pyairtable.models.schema:BaseShares": "operations:list-shares:response:schema",
"pyairtable.models.schema:BaseShares.Info": "operations:list-shares:response:schema:@shares:items",
Expand All @@ -48,6 +48,7 @@
"pyairtable.models.schema:InterfaceInviteLink": "schemas:invite-link",
"pyairtable.models.schema:EnterpriseInfo": "operations:get-enterprise:response:schema",
"pyairtable.models.schema:EnterpriseInfo.EmailDomain": "operations:get-enterprise:response:schema:@emailDomains:items",
"pyairtable.models.schema:EnterpriseInfo.AggregatedIds": "operations:get-enterprise:response:schema:@aggregated",
"pyairtable.models.schema:WorkspaceCollaborators": "operations:get-workspace-collaborators:response:schema",
"pyairtable.models.schema:WorkspaceCollaborators.Restrictions": "operations:get-workspace-collaborators:response:schema:@workspaceRestrictions",
"pyairtable.models.schema:WorkspaceCollaborators.GroupCollaborators": "operations:get-workspace-collaborators:response:schema:@groupCollaborators",
Expand All @@ -63,6 +64,8 @@
"pyairtable.models.schema:Collaborations.InterfaceCollaboration": "schemas:collaborations:@interfaceCollaborations:items",
"pyairtable.models.schema:Collaborations.WorkspaceCollaboration": "schemas:collaborations:@workspaceCollaborations:items",
"pyairtable.models.schema:UserInfo": "operations:get-user-by-id:response:schema",
"pyairtable.models.schema:UserInfo.AggregatedIds": "operations:get-user-by-id:response:schema:@aggregated",
"pyairtable.models.schema:UserInfo.DescendantIds": "operations:get-user-by-id:response:schema:@descendants:*",
"pyairtable.models.schema:UserGroup": "operations:get-user-group:response:schema",
"pyairtable.models.schema:UserGroup.Member": "operations:get-user-group:response:schema:@members:items",
"pyairtable.models.webhook:Webhook": "operations:list-webhooks:response:schema:@webhooks:items",
Expand All @@ -71,15 +74,15 @@
"pyairtable.models.webhook:WebhookPayloads": "operations:list-webhook-payloads:response:schema",
"pyairtable.models.webhook:WebhookPayload": "schemas:webhooks-payload",
"pyairtable.models.webhook:WebhookPayload.ActionMetadata": "schemas:webhooks-action",
"pyairtable.models.webhook:WebhookPayload.FieldChanged": "schemas:webhooks-table-changed:@changedFieldsById:additionalProperties",
"pyairtable.models.webhook:WebhookPayload.FieldInfo": "schemas:webhooks-table-changed:@changedFieldsById:additionalProperties:@current",
"pyairtable.models.webhook:WebhookPayload.RecordChanged": "schemas:webhooks-changed-record:additionalProperties",
"pyairtable.models.webhook:WebhookPayload.RecordCreated": "schemas:webhooks-created-record:additionalProperties",
"pyairtable.models.webhook:WebhookPayload.FieldChanged": "schemas:webhooks-table-changed:@changedFieldsById:*",
"pyairtable.models.webhook:WebhookPayload.FieldInfo": "schemas:webhooks-table-changed:@changedFieldsById:*:@current",
"pyairtable.models.webhook:WebhookPayload.RecordChanged": "schemas:webhooks-changed-record:*",
"pyairtable.models.webhook:WebhookPayload.RecordCreated": "schemas:webhooks-created-record:*",
"pyairtable.models.webhook:WebhookPayload.TableChanged": "schemas:webhooks-table-changed",
"pyairtable.models.webhook:WebhookPayload.TableChanged.ChangedMetadata": "schemas:webhooks-table-changed:@changedMetadata",
"pyairtable.models.webhook:WebhookPayload.TableInfo": "schemas:webhooks-table-changed:@changedMetadata:@current",
"pyairtable.models.webhook:WebhookPayload.TableCreated": "schemas:webhooks-table-created",
"pyairtable.models.webhook:WebhookPayload.ViewChanged": "schemas:webhooks-table-changed:@changedViewsById:additionalProperties",
"pyairtable.models.webhook:WebhookPayload.ViewChanged": "schemas:webhooks-table-changed:@changedViewsById:*",
"pyairtable.models.webhook:CreateWebhook": "operations:create-a-webhook:request:schema",
"pyairtable.models.webhook:CreateWebhookResponse": "operations:create-a-webhook:response:schema",
"pyairtable.models.webhook:WebhookSpecification": "operations:create-a-webhook:request:schema:@specification",
Expand Down Expand Up @@ -115,6 +118,7 @@ def main() -> None:
model_module = importlib.import_module(modname)
model_cls = attrgetter(clsname)(model_module)
initdata_path = initdata_path.replace(":@", ":properties:")
initdata_path = re.sub(r":\*(:|$)", r":additionalProperties\1", initdata_path)
issues.extend(scan_schema(model_cls, initdata.get_nested(initdata_path)))

if not issues:
Expand Down Expand Up @@ -169,14 +173,16 @@ def get_nested(self, path: str, separator: str = ":") -> Any:
"""
get_from = self
traversed = []
while separator in path:
next_key, path = path.split(separator, 1)
traversed.append(next_key)
try:
try:
while separator in path:
next_key, path = path.split(separator, 1)
traversed.append(next_key)
get_from = get_from[next_key]
except KeyError:
raise KeyError(*traversed)
return get_from[path]
traversed.append(path)
return get_from[path]
except KeyError as exc:
exc.args = tuple(traversed)
raise exc

@cached_property
def by_operation(self) -> Dict[str, Dict[str, Any]]:
Expand Down
3 changes: 3 additions & 0 deletions tests/sample_data/EnterpriseInfo.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"createdTime": "2019-01-03T12:33:12.421Z",
"descendantEnterpriseAccountIds": [
"entJ9ZQ5vz9ZQ5vz9"
],
"emailDomains": [
{
"emailDomain": "foobar.com",
Expand Down
3 changes: 2 additions & 1 deletion tests/sample_data/UserInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
{
"baseId": "appLkNDICXNqxSDhG",
"createdTime": "2019-01-03T12:33:12.421Z",
"grantedByUserId": "usrqccqnMB2eHylqB",
"grantedByUserId": "usrogvSbotRtzdtZW",
"interfaceId": "pbdyGA3PsOziEHPDE",
"permissionLevel": "edit"
}
Expand All @@ -38,6 +38,7 @@
],
"id": "usrL2PNC5o3H4lBEi",
"invitedToAirtableByUserId": "usrsOEchC9xuwRgKk",
"isAdmin": true,
"isManaged": true,
"isServiceAccount": false,
"isSsoRequired": true,
Expand Down
Loading

0 comments on commit 9bf990a

Please sign in to comment.