or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

company-management.mdevent-tracking.mdindex.mdjob-management.mdquery-autocompletion.mdsearch-filtering.mdtenant-management.md

tenant-management.mddocs/

0

# Tenant Management

1

2

Multi-tenant data isolation and management for organizations requiring separate data namespaces, enabling secure multi-customer deployments and data segregation. Tenants provide the foundational isolation layer for all Google Cloud Talent API resources.

3

4

## Capabilities

5

6

### Tenant Creation

7

8

Creates new tenant entities that serve as data isolation boundaries for companies, jobs, and all other resources within the Talent API.

9

10

```python { .api }

11

def create_tenant(self, parent: str, tenant: Tenant) -> Tenant:

12

"""

13

Creates a new tenant for data isolation.

14

15

Parameters:

16

- parent (str): Project resource name where tenant will be created

17

- tenant (Tenant): Tenant object with required external_id

18

19

Returns:

20

Tenant: Created tenant with generated resource name

21

22

Raises:

23

- InvalidArgument: Missing required fields or invalid values

24

- AlreadyExists: Tenant with same external_id already exists

25

- PermissionDenied: Insufficient permissions to create tenant

26

"""

27

```

28

29

**Usage Example:**

30

31

```python

32

from google.cloud.talent import TenantServiceClient, Tenant

33

34

client = TenantServiceClient()

35

36

tenant = Tenant(

37

external_id="customer-acme-prod"

38

)

39

40

created_tenant = client.create_tenant(

41

parent="projects/my-project",

42

tenant=tenant

43

)

44

45

print(f"Created tenant: {created_tenant.name}")

46

print(f"External ID: {created_tenant.external_id}")

47

```

48

49

### Tenant Retrieval

50

51

Retrieves individual tenant entities by resource name with complete tenant information and metadata.

52

53

```python { .api }

54

def get_tenant(self, name: str) -> Tenant:

55

"""

56

Retrieves a tenant by its resource name.

57

58

Parameters:

59

- name (str): Full tenant resource name

60

61

Returns:

62

Tenant: Complete tenant object

63

64

Raises:

65

- NotFound: Tenant does not exist

66

- PermissionDenied: Insufficient permissions to view tenant

67

"""

68

```

69

70

**Usage Example:**

71

72

```python

73

tenant = client.get_tenant(

74

name="projects/my-project/tenants/tenant-123"

75

)

76

77

print(f"Tenant external ID: {tenant.external_id}")

78

```

79

80

### Tenant Updates

81

82

Updates existing tenant profiles with field-level control using update masks to specify which tenant attributes should be modified.

83

84

```python { .api }

85

def update_tenant(self, tenant: Tenant, update_mask: FieldMask = None) -> Tenant:

86

"""

87

Updates an existing tenant.

88

89

Parameters:

90

- tenant (Tenant): Tenant object with updated values and resource name

91

- update_mask (FieldMask): Specifies which fields to update

92

93

Returns:

94

Tenant: Updated tenant object

95

96

Raises:

97

- NotFound: Tenant does not exist

98

- InvalidArgument: Invalid field values or update mask

99

- PermissionDenied: Insufficient permissions to update tenant

100

"""

101

```

102

103

**Usage Example:**

104

105

```python

106

from google.protobuf import field_mask_pb2

107

108

# Update tenant external ID

109

tenant.external_id = "customer-acme-production"

110

111

update_mask = field_mask_pb2.FieldMask(paths=["external_id"])

112

113

updated_tenant = client.update_tenant(

114

tenant=tenant,

115

update_mask=update_mask

116

)

117

```

118

119

### Tenant Deletion

120

121

Removes tenant entities from the system. Note that tenants with associated companies or jobs cannot be deleted until all child resources are removed first.

122

123

```python { .api }

124

def delete_tenant(self, name: str) -> None:

125

"""

126

Deletes a tenant entity.

127

128

Parameters:

129

- name (str): Full tenant resource name

130

131

Raises:

132

- NotFound: Tenant does not exist

133

- PermissionDenied: Insufficient permissions to delete tenant

134

- FailedPrecondition: Tenant has associated companies or jobs that must be deleted first

135

"""

136

```

137

138

**Usage Example:**

139

140

```python

141

from google.api_core import exceptions

142

143

try:

144

client.delete_tenant(

145

name="projects/my-project/tenants/tenant-123"

146

)

147

print("Tenant deleted successfully")

148

except exceptions.FailedPrecondition:

149

print("Cannot delete tenant with active companies or jobs")

150

```

151

152

### Tenant Listing

153

154

Lists tenant entities with pagination support for efficient browsing of large tenant datasets within a project.

155

156

```python { .api }

157

def list_tenants(self, parent: str, page_size: int = None,

158

page_token: str = None) -> ListTenantsResponse:

159

"""

160

Lists tenants with pagination.

161

162

Parameters:

163

- parent (str): Project resource name

164

- page_size (int): Maximum number of tenants to return (max 100)

165

- page_token (str): Token for pagination from previous response

166

167

Returns:

168

ListTenantsResponse: Tenants list with pagination token and metadata

169

170

Raises:

171

- InvalidArgument: Invalid page size

172

- PermissionDenied: Insufficient permissions to list tenants

173

"""

174

```

175

176

**Usage Example:**

177

178

```python

179

# List all tenants in a project

180

response = client.list_tenants(

181

parent="projects/my-project",

182

page_size=50

183

)

184

185

for tenant in response.tenants:

186

print(f"Tenant: {tenant.external_id}")

187

print(f" Resource name: {tenant.name}")

188

189

# Handle pagination

190

while response.next_page_token:

191

response = client.list_tenants(

192

parent="projects/my-project",

193

page_token=response.next_page_token,

194

page_size=50

195

)

196

197

for tenant in response.tenants:

198

print(f"Tenant: {tenant.external_id}")

199

```

200

201

## Tenant Data Model

202

203

### Tenant Entity

204

205

```python { .api }

206

class Tenant:

207

"""

208

A tenant resource represents a tenant in the service, which provides

209

data isolation and resource management capabilities for multi-tenant

210

applications.

211

"""

212

name: str = None # Resource name (auto-generated)

213

external_id: str = None # Client-defined tenant identifier (required, max 255 chars)

214

```

215

216

The Tenant entity is intentionally minimal, serving primarily as an isolation boundary. The external_id should be a meaningful identifier that maps to your application's customer or organization identifiers.

217

218

## Request and Response Types

219

220

### Tenant Service Requests

221

222

```python { .api }

223

class CreateTenantRequest:

224

parent: str = None # Project resource name

225

tenant: Tenant = None # Tenant to create

226

227

class GetTenantRequest:

228

name: str = None # Tenant resource name

229

230

class UpdateTenantRequest:

231

tenant: Tenant = None # Tenant with updates

232

update_mask: FieldMask = None # Fields to update

233

234

class DeleteTenantRequest:

235

name: str = None # Tenant resource name

236

237

class ListTenantsRequest:

238

parent: str = None # Project resource name

239

page_size: int = None # Page size (max 100)

240

page_token: str = None # Pagination token

241

```

242

243

### Tenant Service Responses

244

245

```python { .api }

246

class ListTenantsResponse:

247

tenants: List[Tenant] = None # List of tenants

248

next_page_token: str = None # Pagination token for next page

249

metadata: ResponseMetadata = None # Response metadata

250

```

251

252

## Multi-Tenancy Architecture

253

254

Tenants provide the foundational data isolation layer in the Google Cloud Talent API:

255

256

```

257

Project (GCP Project)

258

└── Tenant (Data isolation boundary)

259

├── Company (Job posting organization)

260

│ └── Jobs (Individual job postings)

261

├── Events (User interaction tracking)

262

└── Completion (Query suggestions)

263

```

264

265

### Resource Hierarchy Example

266

267

```python

268

# Single project, multiple tenants for different customers

269

project = "projects/hr-platform-prod"

270

271

# Customer A's isolated data

272

tenant_a = "projects/hr-platform-prod/tenants/customer-a"

273

company_a1 = "projects/hr-platform-prod/tenants/customer-a/companies/acme-corp"

274

job_a1 = "projects/hr-platform-prod/tenants/customer-a/jobs/job-001"

275

276

# Customer B's isolated data

277

tenant_b = "projects/hr-platform-prod/tenants/customer-b"

278

company_b1 = "projects/hr-platform-prod/tenants/customer-b/companies/beta-inc"

279

job_b1 = "projects/hr-platform-prod/tenants/customer-b/jobs/job-001"

280

```

281

282

## Integration with Other Services

283

284

All other Talent API services require a tenant context:

285

286

```python

287

from google.cloud.talent import (

288

TenantServiceClient, CompanyServiceClient,

289

JobServiceClient, EventServiceClient

290

)

291

292

# Create tenant first

293

tenant_client = TenantServiceClient()

294

tenant = tenant_client.create_tenant(

295

parent="projects/my-project",

296

tenant=Tenant(external_id="customer-123")

297

)

298

299

# Use tenant as parent for all other resources

300

company_client = CompanyServiceClient()

301

company = company_client.create_company(

302

parent=tenant.name, # Tenant as parent

303

company=Company(display_name="Acme Corp", external_id="acme")

304

)

305

306

job_client = JobServiceClient()

307

job = job_client.create_job(

308

parent=tenant.name, # Tenant as parent

309

job=Job(company=company.name, requisition_id="job-1", title="Engineer")

310

)

311

312

# Events also scoped to tenant

313

event_client = EventServiceClient()

314

event_client.create_client_event(

315

parent=tenant.name, # Tenant as parent

316

client_event=ClientEvent(...)

317

)

318

```

319

320

## Resource Path Helpers

321

322

The client provides helper methods for constructing tenant resource paths:

323

324

```python { .api }

325

# Class methods for building resource paths

326

@classmethod

327

def tenant_path(cls, project: str, tenant: str) -> str:

328

"""Constructs a fully-qualified tenant resource name."""

329

330

@classmethod

331

def project_path(cls, project: str) -> str:

332

"""Constructs a fully-qualified project resource name."""

333

334

@classmethod

335

def parse_tenant_path(cls, path: str) -> Dict[str, str]:

336

"""Parses a tenant path into its component parts."""

337

```

338

339

**Usage Example:**

340

341

```python

342

# Build resource paths

343

tenant_path = TenantServiceClient.tenant_path(

344

project="my-project",

345

tenant="tenant-123"

346

)

347

348

project_path = TenantServiceClient.project_path(project="my-project")

349

350

# Parse resource paths

351

path_components = TenantServiceClient.parse_tenant_path(tenant_path)

352

print(f"Project: {path_components['project']}")

353

print(f"Tenant: {path_components['tenant']}")

354

```

355

356

## Error Handling

357

358

Tenant management operations can raise several types of exceptions:

359

360

```python

361

from google.api_core import exceptions

362

363

try:

364

tenant = client.create_tenant(parent=parent, tenant=tenant_data)

365

except exceptions.InvalidArgument as e:

366

# Handle validation errors

367

print(f"Invalid tenant data: {e}")

368

except exceptions.AlreadyExists as e:

369

# Handle duplicate external_id

370

print(f"Tenant already exists: {e}")

371

except exceptions.PermissionDenied as e:

372

# Handle authorization errors

373

print(f"Access denied: {e}")

374

```

375

376

Common error scenarios:

377

- **InvalidArgument**: Missing required external_id, invalid field values, malformed resource names

378

- **AlreadyExists**: Duplicate external_id within the same project

379

- **NotFound**: Tenant or project does not exist

380

- **PermissionDenied**: Insufficient IAM permissions for tenant operations

381

- **FailedPrecondition**: Cannot delete tenant with associated companies or jobs

382

- **ResourceExhausted**: API quota limits exceeded

383

384

## Best Practices

385

386

1. **External ID Strategy**: Use meaningful external_id values that map to your application's customer or organization identifiers

387

2. **Naming Conventions**: Establish consistent naming patterns for external_id values (e.g., "customer-{id}-{environment}")

388

3. **Data Isolation**: Ensure your application logic respects tenant boundaries and never mixes data across tenants

389

4. **Resource Cleanup**: When decommissioning a customer, delete all child resources (jobs, companies) before deleting the tenant

390

5. **Environment Separation**: Consider using different external_id patterns for development, staging, and production environments

391

6. **Monitoring**: Implement monitoring to track tenant-level resource usage and API quotas

392

7. **Access Control**: Use IAM policies to control which users and service accounts can manage tenants

393

394

## Multi-Customer SaaS Example

395

396

```python

397

class TalentAPIService:

398

def __init__(self):

399

self.tenant_client = TenantServiceClient()

400

self.company_client = CompanyServiceClient()

401

self.job_client = JobServiceClient()

402

403

def onboard_customer(self, customer_id: str, customer_name: str):

404

"""Onboard a new customer with isolated tenant."""

405

tenant = Tenant(external_id=f"customer-{customer_id}-prod")

406

407

tenant_response = self.tenant_client.create_tenant(

408

parent="projects/hr-saas-platform",

409

tenant=tenant

410

)

411

412

return tenant_response

413

414

def create_customer_company(self, customer_id: str, company_data: dict):

415

"""Create a company within customer's tenant."""

416

tenant_name = f"projects/hr-saas-platform/tenants/customer-{customer_id}-prod"

417

418

company = Company(

419

display_name=company_data["name"],

420

external_id=f"company-{company_data['id']}"

421

)

422

423

return self.company_client.create_company(

424

parent=tenant_name,

425

company=company

426

)

427

```