Metadata-Version: 2.4
Name: gsuite-sdk
Version: 0.2.0
Summary: Unified Google Workspace SDK - Gmail, Calendar, Drive, Sheets, Tasks, Contacts
Author-email: Pablo Alaniz <alanizpablo@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/PabloAlaniz/google-suite
Project-URL: Repository, https://github.com/PabloAlaniz/google-suite
Project-URL: Documentation, https://pabloalaniz.github.io/google-suite/
Keywords: google,workspace,gmail,calendar,drive,sheets,tasks,contacts,oauth,api
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: google-auth>=2.27.0
Requires-Dist: google-auth-oauthlib>=1.2.0
Requires-Dist: google-api-python-client>=2.114.0
Requires-Dist: google-auth-httplib2>=0.2.0
Requires-Dist: httplib2>=0.19.0
Requires-Dist: pydantic>=2.5.0
Requires-Dist: pydantic-settings>=2.1.0
Requires-Dist: tzdata>=2024.1; sys_platform == "win32"
Provides-Extra: dev
Requires-Dist: pytest>=7.4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.8.0; extra == "dev"
Requires-Dist: httpx>=0.27.0; extra == "dev"
Provides-Extra: cloudrun
Requires-Dist: google-cloud-secret-manager>=2.18.0; extra == "cloudrun"
Requires-Dist: google-cloud-logging>=3.9.0; extra == "cloudrun"
Provides-Extra: api
Requires-Dist: fastapi>=0.109.0; extra == "api"
Requires-Dist: uvicorn[standard]>=0.27.0; extra == "api"
Requires-Dist: python-multipart>=0.0.6; extra == "api"
Requires-Dist: email-validator>=2.1.0; extra == "api"
Provides-Extra: cli
Requires-Dist: typer>=0.15.4; extra == "cli"
Requires-Dist: rich>=13.7.0; extra == "cli"
Provides-Extra: pandas
Requires-Dist: pandas>=2.2.2; extra == "pandas"
Provides-Extra: polars
Requires-Dist: polars>=0.20; extra == "polars"
Provides-Extra: async
Requires-Dist: httpx>=0.27.0; extra == "async"
Provides-Extra: all
Requires-Dist: gsuite-sdk[api,async,cli,cloudrun]; extra == "all"
Dynamic: license-file

# Google Suite

[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Docs](https://img.shields.io/badge/docs-gsuite--sdk-indigo.svg)](https://pabloalaniz.github.io/google-suite/)

Unified Python SDK for Google Workspace APIs with Clean Architecture.

## Features

- 🔐 **Unified Auth** - Single OAuth flow for all Google APIs
- 📧 **Gmail** - Send, receive, search, labels, attachments
- 📅 **Calendar** - Events, calendars, scheduling
- 📁 **Drive** - Files, folders, sharing, upload/download
- 📊 **Sheets** - Read/write, upsert, typed rows, validation, conditional formats, charts, pivots, export (engine from [GSpreadManager](docs/MIGRATING_FROM_GSPREADMANAGER.md))
- ✅ **Tasks** - Task lists, tasks, subtasks, due dates, complete/reopen
- 👥 **Contacts** - List, search, create, update and delete contacts (People API)
- 🚀 **REST API** - Single FastAPI gateway for all services
- 💻 **CLI** - Unified command-line interface

## Installation

```bash
pip install gsuite-sdk
```

Optional extras:
```bash
pip install gsuite-sdk[cloudrun]  # Google Cloud Secret Manager support
pip install gsuite-sdk[all]       # All optional dependencies (FastAPI, CLI)
```

## Quick Start

### Prerequisites

You need OAuth credentials from Google Cloud Console. See [Getting Credentials](docs/GETTING_CREDENTIALS.md) for a step-by-step guide.

### Authentication

```python
from gsuite_core import GoogleAuth

# OAuth (interactive, opens browser)
auth = GoogleAuth()
auth.authenticate()

# Or with service account
auth = GoogleAuth.from_service_account("service-account.json")
```

### Gmail

```python
from gsuite_gmail import Gmail, query

gmail = Gmail(auth)

# Get unread messages with fluent API
for message in gmail.get_unread():
    print(f"{message.sender}: {message.subject}")
    message.mark_as_read().archive()  # Fluent chaining!

# Send email with signature
gmail.send(
    to=["recipient@example.com"],
    subject="Hello",
    body="World",
    signature=True,  # Appends your Gmail signature
)

# Search with query builder (inspired by simplegmail)
messages = gmail.search(
    query.newer_than(days=7) & query.has_attachment() & query.from_("boss@company.com")
)

# Or use construct_query for dict-style
messages = gmail.search(query.construct_query(
    unread=True,
    newer_than=(7, "day"),
    labels=["Work"],
))
```

### Drive

```python
from gsuite_drive import Drive

drive = Drive(auth)

# List files
for file in drive.list_files():
    print(f"{file.name} ({file.mime_type})")

# Upload
uploaded = drive.upload("document.pdf")
print(f"Uploaded: {uploaded.web_view_link}")

# Download
file = drive.get("file_id")
file.download("local_copy.pdf")

# Create folder and upload into it
folder = drive.create_folder("My Folder")
drive.upload("file.txt", parent_id=folder.id)
```

### Calendar

```python
from gsuite_calendar import Calendar

calendar = Calendar(auth)

# Get upcoming events
events = calendar.get_upcoming(days=7)
for event in events:
    print(f"{event.start}: {event.summary}")

# Create event
calendar.create_event(
    summary="Meeting",
    start="2026-01-30T10:00:00",
    end="2026-01-30T11:00:00",
)
```

### Sheets

```python
from gsuite_sheets import Sheets

sheets = Sheets(auth)

# Open spreadsheet by title or URL
sheet = sheets.open("My Spreadsheet")
# Or by key
sheet = sheets.open_by_key("1A2B3C...")
# Or by URL
sheet = sheets.open_by_url("https://docs.google.com/spreadsheets/d/...")

# Read values
values = sheet.get_values("Sheet1!A1:D10")
print(values)  # [[row1], [row2], ...]

# Write values
sheet.update_values("Sheet1!A1", [["Name", "Email"], ["John", "john@example.com"]])

# Append rows
sheet.append_values("Sheet1", [["Jane", "jane@example.com"]])

# Batch operations
sheet.batch_update([
    {"range": "Sheet1!A1", "values": [["Header1", "Header2"]]},
    {"range": "Sheet1!A2", "values": [["Data1", "Data2"]]},
])

# Create new spreadsheet
new_sheet = sheets.create("Budget 2026")
print(f"Created: {new_sheet.url}")

# List all spreadsheets
for spreadsheet in sheets.list_spreadsheets():
    print(f"{spreadsheet['name']} - {spreadsheet['id']}")
```

### Tasks and Contacts

Tasks and Contacts are opt-in: their scopes are not in the default login
(contacts is a sensitive scope). Log in with them and enable the **Google Tasks
API** / **People API** in your Cloud project:

```bash
gsuite auth login --force --scopes default,tasks,contacts
```

```python
from datetime import date

from gsuite_contacts import Contacts
from gsuite_tasks import Tasks

tasks = Tasks(auth)
task = tasks.create_task("Pay rent", due=date(2026, 2, 1))
for t in tasks.list_tasks(show_completed=False):
    print(t.title, t.due, "overdue" if t.is_overdue else "")
tasks.complete_task(task.id)

contacts = Contacts(auth)
for c in contacts.search("ana"):
    print(c.display_name, c.email, c.phone)
ana = contacts.create(given_name="Ana", emails=["ana@example.com"])
contacts.update(ana.id, phones=["+54 11 5555-5555"])
```

### REST API

```bash
# Start unified API server
gsuite serve --port 8080

# Or with Docker
docker run -p 8080:8080 gsuite-api
```

```bash
# All services under one roof
curl http://localhost:8080/gmail/messages/unread
curl http://localhost:8080/calendar/events/upcoming
curl http://localhost:8080/drive/files
curl http://localhost:8080/sheets/list
curl http://localhost:8080/sheets/{spreadsheet_id}/values/Sheet1!A1:D10
curl http://localhost:8080/tasks/lists/@default/tasks
curl "http://localhost:8080/contacts/search?q=ana"
```

**Interactive API docs available at `/docs` when server is running.** See [API Documentation](#api-documentation) section below for details.

### CLI

```bash
# Authentication
gsuite auth login              # Opens browser for OAuth
gsuite auth status             # Check auth status
gsuite auth export             # Export token for Cloud Run

# Gmail
gsuite gmail list --unread     # List unread messages
gsuite gmail read MSG_ID       # Read specific message
gsuite gmail send --to user@example.com --subject "Hi" --body "Hello"
gsuite gmail search "from:boss has:attachment"
gsuite gmail labels            # List all labels

# Calendar  
gsuite calendar list --days 7  # Upcoming events
gsuite calendar today          # Today's events
gsuite calendar week           # Week view
gsuite calendar create "Meeting" --start "2026-01-30 10:00"
gsuite calendar calendars      # List calendars

# Sheets
gsuite sheets list             # List all spreadsheets
gsuite sheets open "Budget"    # Open by title
gsuite sheets read SHEET_ID "Sheet1!A1:D10"  # Read range
gsuite sheets write SHEET_ID "Sheet1!A1" "Name,Email"
gsuite sheets append SHEET_ID "Sheet1" "John,john@example.com"
gsuite sheets create "New Sheet"  # Create spreadsheet

# Tasks (login with --scopes default,tasks)
gsuite tasks lists             # Task lists
gsuite tasks ls --due-before 2026-02-28
gsuite tasks add "Pay rent" --due 2026-02-01
gsuite tasks done TASK_ID

# Contacts (login with --scopes default,contacts)
gsuite contacts search ana
gsuite contacts add -g Ana -e ana@example.com -p "+54 11 5555-5555"
gsuite contacts show CONTACT_ID

# Server
gsuite serve --port 8080       # Start REST API
gsuite status                  # Overall status
```

## Architecture

```
google-suite/
├── packages/
│   ├── core/           # Shared auth, config, token storage
│   ├── gmail/          # Gmail client + query builder
│   ├── calendar/       # Calendar client
│   ├── drive/          # Drive client (upload, download, share)
│   ├── sheets/         # Sheets client + engine (from GSpreadManager)
│   ├── tasks/          # Tasks client
│   └── contacts/       # Contacts client (People API)
├── api/                # Unified FastAPI REST gateway
├── cli/                # Unified CLI (Typer + Rich)
├── gsuite-sdk/         # AI agent skill (OpenClaw compatible)
├── docs/               # Documentation site (MkDocs)
└── tests/integration/  # Opt-in tests against a real Google account
```

### Design Principles

- **Clean Architecture** - Domain entities, interfaces, infrastructure separation
- **Provider Agnostic** - Swap implementations via interfaces
- **Shared Auth** - One OAuth flow grants access to all services
- **Independent Packages** - Install only what you need
- **Unified Gateway** - Single API/CLI for all services

For detailed architecture decisions and design patterns, see [Architecture Documentation](docs/ARCHITECTURE.md).

## AI Agent Skill

This repo includes an [OpenClaw](https://openclaw.ai)-compatible skill for AI agents:

```
gsuite-sdk/
└── SKILL.md      # Usage documentation and metadata for agents
```

Agents can use this skill to interact with Google Workspace on behalf of users.

## Standalone Repos

This monorepo consolidates and extends these standalone projects:

- [Gmail-API](https://github.com/PabloAlaniz/Gmail-API) - Standalone Gmail API
- [Calendar-API](https://github.com/PabloAlaniz/Calendar-API) - Standalone Calendar API

The standalone repos remain functional for existing deployments.

## Configuration

Environment variables (prefix `GSUITE_`):

| Variable | Description |
|----------|-------------|
| `GSUITE_CREDENTIALS_FILE` | OAuth credentials JSON path |
| `GSUITE_TOKEN_STORAGE` | `sqlite` or `secretmanager` |
| `GSUITE_TOKEN_DB_PATH` | SQLite token database path |
| `GSUITE_GCP_PROJECT_ID` | GCP project (for Secret Manager) |
| `GSUITE_API_KEY` | API key for REST endpoints (required unless `GSUITE_ALLOW_NO_API_KEY=true`) |
| `GSUITE_CORS_ORIGINS` | Comma-separated origins allowed by CORS (off by default) |

See [api/README.md](api/README.md#configuration) for every REST API setting.

## Development

```bash
# Clone
git clone https://github.com/PabloAlaniz/google-suite.git
cd google-suite

# Install everything, pinned by uv.lock
uv sync --all-extras

# Run tests
uv run pytest

# Lint
uv run ruff check packages api cli
```

## Deployment

### Cloud Run (GCP)

Deploy the REST API to Cloud Run with Secret Manager for token storage:

```bash
# Build and push Docker image (from the repo root)
docker build -f api/Dockerfile -t gcr.io/YOUR_PROJECT/gsuite-api .
docker push gcr.io/YOUR_PROJECT/gsuite-api

# Deploy to Cloud Run
gcloud run deploy gsuite-api \
  --image gcr.io/YOUR_PROJECT/gsuite-api \
  --platform managed \
  --region us-central1 \
  --set-env-vars GSUITE_TOKEN_STORAGE=secretmanager \
  --set-env-vars GSUITE_GCP_PROJECT_ID=YOUR_PROJECT \
  --set-secrets GSUITE_API_KEY=gsuite-api-key:latest \
  --allow-unauthenticated
```

The API rejects every request until `GSUITE_API_KEY` is set. If you protect
the service with IAM instead (`--no-allow-unauthenticated`), set
`GSUITE_ALLOW_NO_API_KEY=true` rather than the key.

**Note:** For Secret Manager token storage, ensure your Cloud Run service account has `secretmanager.versions.access` permission.

### Docker Compose

For local development or self-hosted deployment:

```yaml
version: '3.8'
services:
  gsuite-api:
    build:
      context: .
      dockerfile: api/Dockerfile
    ports:
      - "8080:8080"
    environment:
      GSUITE_CREDENTIALS_FILE: /secrets/credentials.json
      GSUITE_TOKEN_DB_PATH: /data/tokens.db
      GSUITE_API_KEY: your-api-key-here
    volumes:
      - ./credentials.json:/secrets/credentials.json:ro
      - ./data:/data
```

## API Documentation

When running the REST API server, interactive API docs are available at:

- **Swagger UI**: `http://localhost:8080/docs`
- **ReDoc**: `http://localhost:8080/redoc`

## Troubleshooting

### Token refresh failed

**Problem:** `TokenRefreshError: Failed to refresh token`

**Solution:**
- Delete the token database: `rm ~/.gsuite/tokens.db` (or your configured path)
- Re-authenticate: `gsuite auth login`
- For service accounts, verify the JSON file hasn't expired

### Missing scopes

**Problem:** `HttpError 403: Insufficient Permission`

**Solution:**
- Check required scopes in error message
- Re-authenticate with additional scopes:
  ```bash
  gsuite auth login --scopes gmail.readonly,calendar,drive
  ```
- Or update `GSUITE_DEFAULT_SCOPES` environment variable

### Credentials not found

**Problem:** `CredentialsNotFoundError: OAuth credentials file not found`

**Solution:**
- Download OAuth credentials from [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
- Save as `credentials.json` in working directory
- Or set `GSUITE_CREDENTIALS_FILE` environment variable

### Import errors

**Problem:** `ModuleNotFoundError: No module named 'gsuite_gmail'`

**Solution:**
- Install the package: `pip install gsuite-sdk`
- Or for development: `uv sync --all-extras` (see [CONTRIBUTING.md](CONTRIBUTING.md))

## License

MIT - see [LICENSE](LICENSE)

## Author

Pablo Alaniz ([@PabloAlaniz](https://github.com/PabloAlaniz))
