0
# Google Directory APIs
1
2
Direct access to Google Directory API resources with authentication, pagination, error handling, and retry logic. These classes provide low-level access to users, groups, and group membership data from Google Workspace.
3
4
## Capabilities
5
6
### Core API Wrapper
7
8
The main API class that handles Google Directory API authentication and request execution.
9
10
```python { .api }
11
class API:
12
"""
13
Core Google Directory API client with authentication handling.
14
15
Manages OAuth 2.0 and Service Account authentication, constructs
16
Google API service resources, and executes API requests with
17
automatic retry and rate limiting.
18
"""
19
20
def __init__(self, credentials: Mapping[str, Any]):
21
"""
22
Initialize the Google Directory API client.
23
24
Parameters:
25
- credentials: Dict containing authentication credentials
26
For OAuth: client_id, client_secret, refresh_token
27
For Service Account: credentials_json, email
28
"""
29
30
def get(self, name: str, params: Dict = None) -> Dict:
31
"""
32
Execute Google Directory API GET request with retry logic.
33
34
Parameters:
35
- name: API resource name ('users', 'groups', 'members')
36
- params: Dict of query parameters for the API request
37
38
Returns:
39
- Dict: API response containing requested data
40
41
Features automatic retry with exponential backoff for rate limits
42
and quota exceeded errors using the backoff decorator.
43
"""
44
45
@staticmethod
46
def _load_account_info(credentials_json: str) -> Dict:
47
"""
48
Parse service account credentials from JSON string.
49
50
Parameters:
51
- credentials_json: JSON string containing service account info
52
53
Returns:
54
- Dict: Parsed account information
55
"""
56
57
def _obtain_service_account_creds(self):
58
"""
59
Obtain Google service account credentials for domain-wide delegation.
60
61
Uses the credentials_json and email from the configuration to create
62
service account credentials with the required Directory API scopes
63
and subject delegation for the admin email.
64
"""
65
66
def _obtain_web_app_creds(self):
67
"""
68
Obtain Google OAuth 2.0 web application credentials.
69
70
Uses client_id, client_secret, and refresh_token from configuration
71
to create OAuth credentials. Automatically refreshes tokens if expired.
72
"""
73
74
def _obtain_creds(self):
75
"""
76
Determine credential type and obtain appropriate authentication.
77
78
Automatically detects whether to use service account or OAuth
79
credentials based on the presence of credentials_json or client_id
80
in the configuration.
81
"""
82
83
def _construct_resource(self):
84
"""
85
Construct the Google Directory API service resource.
86
87
Obtains credentials if not already available and builds the
88
'admin' service with 'directory_v1' API version.
89
"""
90
91
def _get_resource(self, name: str):
92
"""
93
Get Google Directory API resource by name.
94
95
Parameters:
96
- name: Resource name ('users', 'groups', 'members')
97
98
Returns:
99
- Google API resource object for the specified resource type
100
"""
101
```
102
103
### Stream API Base Class
104
105
Abstract base class providing common functionality for all Google Directory stream APIs.
106
107
```python { .api }
108
class StreamAPI(ABC):
109
"""
110
Abstract base class for Google Directory stream APIs.
111
112
Provides common pagination, response processing, and data reading
113
functionality for all stream implementations.
114
"""
115
116
results_per_page = 100 # Default page size for API requests
117
118
def __init__(self, api: API, *args, **kwargs):
119
"""
120
Initialize stream API with core API instance.
121
122
Parameters:
123
- api: Core API instance for making requests
124
"""
125
126
@abstractmethod
127
def list(self, fields: Sequence[str] = None) -> Iterator[dict]:
128
"""
129
Iterate over entities for this stream.
130
131
Parameters:
132
- fields: Optional list of fields to include in response
133
134
Returns:
135
- Iterator[dict]: Iterator yielding individual records
136
"""
137
138
@abstractmethod
139
def process_response(self, response: Dict) -> Iterator[dict]:
140
"""
141
Process Google Directory API response and extract records.
142
143
Parameters:
144
- response: Raw API response dict
145
146
Returns:
147
- Iterator[dict]: Iterator yielding processed records
148
"""
149
150
def _api_get(self, resource: str, params: Dict = None):
151
"""
152
Internal method to call the core API get method.
153
154
Parameters:
155
- resource: API resource name
156
- params: Query parameters for the request
157
158
Returns:
159
- Dict: API response data
160
"""
161
162
def read(self, getter: Callable, params: Dict = None) -> Iterator:
163
"""
164
Read data using getter function with automatic pagination.
165
166
Parameters:
167
- getter: Function to call for each page of data
168
- params: Initial parameters for API request
169
170
Returns:
171
- Iterator: Iterator yielding records across all pages
172
173
Handles nextPageToken pagination automatically.
174
"""
175
```
176
177
### Users API
178
179
API for accessing Google Directory users data.
180
181
```python { .api }
182
class UsersAPI(StreamAPI):
183
"""
184
API for accessing Google Directory users.
185
186
Provides access to user accounts in the Google Workspace domain
187
including profile information, organizational data, and metadata.
188
"""
189
190
def list(self, fields: Sequence[str] = None) -> Iterator[dict]:
191
"""
192
List all users in the Google Workspace domain.
193
194
Parameters:
195
- fields: Optional list of user fields to include
196
197
Returns:
198
- Iterator[dict]: Iterator yielding user records
199
200
Each user record contains standard Google Directory user fields
201
including id, primaryEmail, name, orgUnitPath, etc.
202
"""
203
204
def process_response(self, response: Dict) -> Iterator[dict]:
205
"""
206
Extract users from API response.
207
208
Parameters:
209
- response: Raw API response containing users data
210
211
Returns:
212
- Iterator[dict]: Iterator yielding individual user records
213
"""
214
```
215
216
### Groups API
217
218
API for accessing Google Directory groups data.
219
220
```python { .api }
221
class GroupsAPI(StreamAPI):
222
"""
223
API for accessing Google Directory groups.
224
225
Provides access to groups in the Google Workspace domain including
226
group metadata, settings, and organizational information.
227
"""
228
229
def list(self, fields: Sequence[str] = None) -> Iterator[dict]:
230
"""
231
List all groups in the Google Workspace domain.
232
233
Parameters:
234
- fields: Optional list of group fields to include
235
236
Returns:
237
- Iterator[dict]: Iterator yielding group records
238
239
Each group record contains standard Google Directory group fields
240
including id, email, name, description, etc.
241
"""
242
243
def process_response(self, response: Dict) -> Iterator[dict]:
244
"""
245
Extract groups from API response.
246
247
Parameters:
248
- response: Raw API response containing groups data
249
250
Returns:
251
- Iterator[dict]: Iterator yielding individual group records
252
"""
253
```
254
255
### Group Members API
256
257
API for accessing Google Directory group membership data.
258
259
```python { .api }
260
class GroupMembersAPI(StreamAPI):
261
"""
262
API for accessing Google Directory group memberships.
263
264
Provides access to group membership relationships, iterating through
265
all groups and fetching members for each group.
266
"""
267
268
def list(self, fields: Sequence[str] = None) -> Iterator[dict]:
269
"""
270
List members for all groups in the domain.
271
272
Parameters:
273
- fields: Optional list of member fields to include
274
275
Returns:
276
- Iterator[dict]: Iterator yielding group member records
277
278
Iterates through all groups and fetches membership data for each,
279
yielding individual member records with group context.
280
"""
281
282
def process_response(self, response: Dict) -> Iterator[dict]:
283
"""
284
Extract group members from API response.
285
286
Parameters:
287
- response: Raw API response containing members data
288
289
Returns:
290
- Iterator[dict]: Iterator yielding individual member records
291
292
Returns empty list if no members found in response.
293
"""
294
```
295
296
### Utility Functions
297
298
Helper functions for API error handling and retry logic.
299
300
```python { .api }
301
def rate_limit_handling(error):
302
"""
303
Error handler for backoff retry logic.
304
305
Determines whether API errors should trigger retries based on
306
HTTP status codes and error reasons.
307
308
Parameters:
309
- error: GoogleApiHttpError instance
310
311
Returns:
312
- bool: True if error should not be retried, False to trigger retry
313
314
Retries are disabled for specific rate limit cases:
315
- HTTP 403 with reason 'quotaExceeded'
316
- HTTP 429 with reason 'rateLimitExceeded'
317
318
All other errors will trigger retry with exponential backoff.
319
"""
320
```
321
322
## Usage Examples
323
324
### Direct API Access
325
326
```python
327
from source_google_directory.api import API, UsersAPI, GroupsAPI, GroupMembersAPI
328
329
# OAuth credentials
330
credentials = {
331
"client_id": "your-client-id",
332
"client_secret": "your-client-secret",
333
"refresh_token": "your-refresh-token"
334
}
335
336
# Initialize core API
337
api = API(credentials)
338
339
# Test direct API call
340
response = api.get("users", params={"customer": "my_customer"})
341
print(f"Found {len(response.get('users', []))} users")
342
```
343
344
### Stream API Usage
345
346
```python
347
# Initialize stream APIs
348
users_api = UsersAPI(api)
349
groups_api = GroupsAPI(api)
350
members_api = GroupMembersAPI(api)
351
352
# Iterate through users
353
for user in users_api.list():
354
print(f"User: {user['primaryEmail']} ({user['name']['fullName']})")
355
356
# Iterate through groups
357
for group in groups_api.list():
358
print(f"Group: {group['email']} - {group['name']}")
359
360
# Iterate through group memberships
361
for member in members_api.list():
362
print(f"Member: {member['email']} in group {member.get('groupId', 'N/A')}")
363
```
364
365
### Service Account Configuration
366
367
```python
368
# Service account credentials
369
service_credentials = {
370
"credentials_json": '''{
371
"type": "service_account",
372
"project_id": "your-project",
373
"private_key_id": "...",
374
"private_key": "...",
375
"client_email": "...",
376
"client_id": "...",
377
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
378
"token_uri": "https://oauth2.googleapis.com/token"
379
}''',
380
"email": "admin@yourdomain.com"
381
}
382
383
api = API(service_credentials)
384
```
385
386
## Authentication Requirements
387
388
### Required OAuth Scopes
389
390
```python
391
SCOPES = [
392
"https://www.googleapis.com/auth/admin.directory.user.readonly",
393
"https://www.googleapis.com/auth/admin.directory.group.readonly"
394
]
395
```
396
397
### Prerequisites
398
399
- **OAuth Method**: OAuth 2.0 application configured in Google Cloud Console
400
- **Service Account Method**: Service account with domain-wide delegation enabled
401
- **Admin Access**: Credentials must have Google Workspace admin privileges
402
- **API Enable**: Google Admin SDK Directory API must be enabled in the project
403
404
## Error Handling
405
406
The APIs implement comprehensive error handling with the following features:
407
408
### Retry Logic
409
- **Automatic Retry**: Uses `@backoff.on_exception` decorator with exponential backoff
410
- **Max Retries**: Up to 7 retry attempts for transient errors
411
- **Backoff Strategy**: Exponential backoff starting from 1 second
412
- **Exception Type**: Catches `GoogleApiHttpError` exceptions
413
414
### Rate Limit Handling
415
- **HTTP 403 quotaExceeded**: No retry (permanent failure)
416
- **HTTP 429 rateLimitExceeded**: No retry (permanent failure)
417
- **All other errors**: Automatic retry with exponential backoff
418
- **Custom Handler**: Uses `rate_limit_handling()` function to determine retry eligibility
419
420
### Authentication Management
421
- **OAuth Token Refresh**: Automatic refresh when tokens expire
422
- **Service Account**: Automatic credential validation and delegation setup
423
- **Error Detection**: Graceful handling of authentication failures
424
425
### Error Propagation
426
- **Meaningful Messages**: Detailed error information for debugging
427
- **Health Check Integration**: Connection errors surface through health_check() method
428
- **Exception Details**: Preserves original Google API error context
429
430
### Retry Configuration
431
```python
432
@backoff.on_exception(
433
backoff.expo, # Exponential backoff strategy
434
GoogleApiHttpError, # Exception type to catch
435
max_tries=7, # Maximum retry attempts
436
giveup=rate_limit_handling # Custom retry decision function
437
)
438
```
439
440
Rate-limited requests that exceed quotas are treated as permanent failures and will not be retried, while transient network or server errors trigger automatic retry with increasing delays.