feat(contacts): add list/search Workspace domain directory people#834
feat(contacts): add list/search Workspace domain directory people#834abedegno wants to merge 3 commits into
Conversation
Adds two read-only tools backed by the People API endpoints listDirectoryPeople and searchDirectoryPeople, which surface the Workspace domain directory (colleagues and admin-managed shared domain contacts) - distinct from the user's personal contacts already covered by list_contacts and search_contacts. - auth/scopes.py: add DIRECTORY_READONLY_SCOPE (https://www.googleapis.com/auth/directory.readonly) and include it in CONTACTS_SCOPES and the contacts read-only tool scope set. - auth/service_decorator.py: expose a "directory_read" scope key so the new tools require only the directory scope. - gcontacts/contacts_tools.py: add list_directory_people and search_directory_people, plus a small helper that resolves friendly source aliases ('profile' / 'contact') to the API's DIRECTORY_SOURCE_TYPE_* values. Default readMask additionally requests locations and userDefined so admin-synced HR fields (such as start date or employee id) surface when configured. - tests/gcontacts/test_directory_tools.py: 17 unit tests covering source resolution, default and explicit params, page-size clamping, pagination, empty results and input validation. Domain directory access requires a Google Workspace account; consumer Google accounts cannot grant directory.readonly.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds OAuth scope configuration and two new MCP tools to enable listing and searching people in a Google Workspace directory via the Google People API, backed by comprehensive tests. ChangesDirectory People Listing and Search
Sequence DiagramsequenceDiagram
participant Client
participant list_directory_people
participant _resolve_directory_sources
participant PeopleAPI as People API
participant ClientResponse as Client Response
Client->>list_directory_people: page_size, sources, merge_contact_into_profile
list_directory_people->>_resolve_directory_sources: sources aliases
_resolve_directory_sources-->>list_directory_people: resolved sources
list_directory_people->>PeopleAPI: listDirectoryPeople(readMask, sources, pageSize, mergeSources, pageToken)
PeopleAPI-->>list_directory_people: people[], nextPageToken
list_directory_people-->>ClientResponse: formatted text + nextPageToken?
Client->>search_directory_people: query, page_size, sources, merge_contact_into_profile
search_directory_people->>_resolve_directory_sources: sources aliases
_resolve_directory_sources-->>search_directory_people: resolved sources
search_directory_people->>PeopleAPI: searchDirectoryPeople(query, readMask, sources, pageSize, mergeSources, pageToken)
PeopleAPI-->>search_directory_people: people[], nextPageToken
search_directory_people-->>ClientResponse: formatted text + nextPageToken?
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/gcontacts/test_directory_tools.py (1)
203-267: ⚡ Quick winConsider additional test coverage for search_directory_people to match list_directory_people.
The test suite for
list_directory_peopleincludes tests formerge_contact_into_profile=False(line 102),page_tokenpassthrough (line 145), and negativepage_sizerejection (line 134). Adding equivalent tests forsearch_directory_peoplewould provide symmetrical coverage and catch potential regressions in either tool.💚 Suggested additional test cases
def test_merge_disabled_omits_param(self): svc = MagicMock() svc.people.return_value.searchDirectoryPeople.return_value.execute.return_value = { "people": [] } run( search_directory_people( service=svc, user_google_email="me@example.com", query="test", merge_contact_into_profile=False, ) ) kwargs = svc.people.return_value.searchDirectoryPeople.call_args.kwargs assert "mergeSources" not in kwargs def test_passes_page_token(self): svc = MagicMock() svc.people.return_value.searchDirectoryPeople.return_value.execute.return_value = { "people": [] } run( search_directory_people( service=svc, user_google_email="me@example.com", query="test", page_token="tok123", ) ) kwargs = svc.people.return_value.searchDirectoryPeople.call_args.kwargs assert kwargs["pageToken"] == "tok123" def test_rejects_negative_page_size(self): svc = MagicMock() with pytest.raises(UserInputError): run( search_directory_people( service=svc, user_google_email="me@example.com", query="test", page_size=-1, ) )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/gcontacts/test_directory_tools.py` around lines 203 - 267, Add three tests for search_directory_people to mirror list_directory_people: (1) test_merge_disabled_omits_param — call search_directory_people(service=svc, user_google_email="me@example.com", query="test", merge_contact_into_profile=False) with svc mocked and assert "mergeSources" not in svc.people.return_value.searchDirectoryPeople.call_args.kwargs; (2) test_passes_page_token — call search_directory_people(..., page_token="tok123") and assert kwargs["pageToken"] == "tok123"; (3) test_rejects_negative_page_size — call search_directory_people(..., page_size=-1) inside pytest.raises(UserInputError) to ensure negative sizes raise; use the same MagicMock pattern and run(...) helper as other tests to locate where to add them.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/gcontacts/test_directory_tools.py`:
- Around line 203-267: Add three tests for search_directory_people to mirror
list_directory_people: (1) test_merge_disabled_omits_param — call
search_directory_people(service=svc, user_google_email="me@example.com",
query="test", merge_contact_into_profile=False) with svc mocked and assert
"mergeSources" not in
svc.people.return_value.searchDirectoryPeople.call_args.kwargs; (2)
test_passes_page_token — call search_directory_people(..., page_token="tok123")
and assert kwargs["pageToken"] == "tok123"; (3) test_rejects_negative_page_size
— call search_directory_people(..., page_size=-1) inside
pytest.raises(UserInputError) to ensure negative sizes raise; use the same
MagicMock pattern and run(...) helper as other tests to locate where to add
them.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a0e01610-3053-4309-bcfa-bce48c6f78ec
📒 Files selected for processing (4)
auth/scopes.pyauth/service_decorator.pygcontacts/contacts_tools.pytests/gcontacts/test_directory_tools.py
Mirror list_directory_people tests for search_directory_people: merge-disabled omits mergeSources, page_token passthrough, and invalid page_size rejection.
Summary
Adds two read-only tools backed by the People API endpoints
listDirectoryPeople and searchDirectoryPeople. These surface the Google
Workspace domain directory (colleagues and admin-managed shared domain
contacts), which is distinct from the user's personal contacts already
covered by list_contacts and search_contacts.
Domain directory access requires a Google Workspace account. Consumer
Google accounts cannot grant directory.readonly.
Changes
(https://www.googleapis.com/auth/directory.readonly) and include it in
CONTACTS_SCOPES and the contacts read-only tool scope set.
new tools require only the directory scope.
search_directory_people, plus a helper that resolves friendly source
aliases ('profile' / 'contact') to the API's DIRECTORY_SOURCE_TYPE_*
values. The default readMask additionally requests locations and
userDefined so admin-synced HR fields (such as start date or employee
id) surface when configured.
resolution, default and explicit params, page-size clamping,
pagination, empty results, and input validation.
New tools
Workspace domain directory. Supports page_size (max 1000), pagination,
source selection (profile and/or contact), and optional merging of
shared-contact entries into matching profiles.
by name, email, organization, and title. Supports page_size (max 500),
pagination, source selection, and merge.
Both tools are read-only and request only directory.readonly.
Testing
All 18 unit tests pass. Each tool is invoked through its unwrapped async
function with a mocked People API client.
Notes
Adding DIRECTORY_READONLY_SCOPE to the shared contacts scope set means
existing contacts users will be prompted to re-consent on next auth, and
consumer (non-Workspace) accounts will see a Workspace-only scope they
cannot grant. Happy to scope this down to only the two new tools if
preferred.
Summary by CodeRabbit
New Features
Tests