Pattern-033: Notion Publishing

Status

Proven - Implemented and validated with real API integration testing

Context

Publishing markdown content to Notion workspaces is a core functionality for Piper Morgan’s knowledge management capabilities. This pattern addresses:

Pattern Description

The Notion Publishing pattern implements a three-layer architecture:

  1. CLI Command Layer: User-facing interface (cli/commands/publish.py)
  2. Publisher Service Layer: Platform-agnostic orchestration (services/publishing/publisher.py)
  3. Converter Layer: Format-specific transformations (services/publishing/converters/)

Core capabilities:

Implementation

Structure

cli/commands/
  publish.py              # CLI interface with argparse
services/publishing/
  publisher.py            # Main orchestration service
  converters/
    markdown_to_notion.py # Markdown → Notion blocks conversion
services/integrations/
  notion/
    notion_integration_router.py  # Notion API integration
tests/publishing/
  test_publish_command.py # TDD suite with real API calls

Code Example

CLI Usage:

# Publish to Notion page
python cli/commands/publish.py publish README.md --to notion --location parent-id

# Publish to Notion database (for ADRs)
python cli/commands/publish.py publish docs/adrs/adr-026.md --to notion --database db-id

Service Layer:

from services.publishing.publisher import Publisher

publisher = Publisher()

# Publish markdown file
result = await publisher.publish(
    file_path="docs/guide.md",
    platform="notion",
    location=parent_page_id,
    format="markdown"
)

if result["success"]:
    print(f"Published: {result['url']}")
    if result.get("warnings"):
        print(f"Warnings: {result['warnings']}")
else:
    print(f"Failed: {result['error']}")

Markdown Conversion:

from services.publishing.converters.markdown_to_notion import (
    convert_markdown_to_notion_blocks
)

markdown_content = """
# My Document

This is a paragraph with **bold** text.

- List item 1
- List item 2
"""

result = convert_markdown_to_notion_blocks(markdown_content)
# Returns: {
#   "success": True,
#   "blocks": [...notion block objects...],
#   "warnings": ["Unsupported element: ..." if any]
# }

Configuration

Environment variables:

NOTION_API_KEY=secret_...  # Notion integration token

User configuration (config/PIPER.user.md):

## Notion Configuration

- **parent_id.test**: [test-parent-page-id]
- **parent_id.docs**: [docs-parent-page-id]
- **database_id.adrs**: [adr-database-id]

Usage Guidelines

When to Use

When NOT to Use

Best Practices

Environment Loading:

# CRITICAL: Load environment variables FIRST in CLI commands
from dotenv import load_dotenv
load_dotenv()

# Then import services that depend on environment
from services.publishing.publisher import Publisher

Error Handling:

try:
    result = await publisher.publish(...)
    if result["success"]:
        # Success path with warnings
        if result.get("warnings"):
            for warning in result["warnings"]:
                print(f"⚠️  {warning}")
    else:
        # Failure path with actionable error
        print(f"❌ {result['error']}")
except ValueError as e:
    # User errors (invalid parent, bad configuration)
    print(f"Configuration error: {e}")
except Exception as e:
    # Unexpected errors
    print(f"Unexpected error: {e}")

Test-Driven Development:

# Tests MUST use real API calls, not mocks for core functionality
@pytest.mark.integration
@pytest.mark.asyncio
async def test_publish_creates_actual_notion_page(test_parent_id, test_prefix):
    """CRITICAL: Verify publishing ACTUALLY creates page in Notion"""
    publisher = Publisher()
    result = await publisher.publish(
        file_path=test_file,
        platform="notion",
        location=test_parent_id
    )

    # Verify in Notion (real API call)
    notion = NotionMCPAdapter()
    await notion.connect()
    page = await notion.get_page(result["page_id"])
    assert page is not None, "Created page not found in Notion"

Supported Markdown Elements (MVP scope):

Examples in Codebase

Primary Usage

Test Examples

Complements

Dependencies

Migration Notes

From Issue #135 (CORE-NOTN-PUBLISH)

Implemented features:

From Handoff Document (2025-08-29)

Key learning preserved:

References

Documentation

Usage Analysis

External References


Pattern created: October 8, 2025 Status: Proven - Implemented and validated with real API testing Maintenance: Active - Core knowledge management functionality