Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix enterprise.info and enterprise.users after new API params #405

Merged
merged 1 commit into from
Nov 12, 2024
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
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
Loading