adobe-to-docusign-migrator/docs/IMPLEMENTATION-PLAN.md

10 KiB

Implementation Plan — Adobe → DocuSign Migrator v2

Created: 2026-04-17


Objective

Extend the CLI migration pipeline with:

  1. Idempotent upload — update the most recently modified DocuSign template with the same name instead of always creating a new one.
  2. Web UI — browser-based app that lets users authenticate to both platforms, browse templates side-by-side, and run migrations with live feedback.

Architecture Overview

adobe-to-docusign-migrator/
├── src/                        # Core pipeline (existing)
│   ├── compose_docusign_template.py
│   ├── upload_docusign_template.py   ← Phase 1: upsert logic added
│   ├── adobe_api.py
│   ├── docusign_auth.py
│   └── ...
├── web/                        # New — FastAPI web app
│   ├── app.py                  # FastAPI entrypoint
│   ├── config.py               # Env/settings
│   ├── session.py              # Session middleware
│   ├── routers/
│   │   ├── auth.py             # Adobe + DocuSign OAuth
│   │   ├── templates.py        # Listing + status API
│   │   └── migrate.py          # Migration trigger + history
│   └── static/
│       ├── index.html          # Main SPA page
│       ├── app.js              # Vanilla JS app
│       └── style.css
├── tests/
│   ├── test_mapping.py         # Existing field-mapping unit tests
│   ├── test_upload_upsert.py   # Phase 1: upsert logic
│   ├── test_api_health.py      # Phase 2: health endpoint
│   ├── test_api_auth.py        # Phase 3: auth endpoints
│   ├── test_api_templates.py   # Phase 4: template listing
│   ├── test_api_migrate.py     # Phase 5: migration API
│   └── test_e2e.py             # Phase 7: full pipeline e2e
└── docs/
    ├── IMPLEMENTATION-PLAN.md  ← this file
    └── agent-harness/
        └── EXECUTION-BOARD.md

Tech Stack

Layer Choice Reason
Backend FastAPI Matches existing Python; async; auto-generates OpenAPI docs
Sessions starlette-sessions + itsdangerous Lightweight, no DB needed
Frontend Vanilla HTML/CSS/JS No build tooling; straightforward for this scope
Testing pytest + httpx + respx FastAPI's recommended test stack; respx for mocking HTTP
HTTP mocking respx Intercepts httpx calls for Adobe/DocuSign API mocks

Phase 1 — Idempotent Upload

Goal: upload_docusign_template.py should update the most recently modified DocuSign template with the same name, rather than always creating a new one.

Logic:

  1. After loading the template JSON, extract its name.
  2. Call GET /v2.1/accounts/{accountId}/templates?search_text={name} to list matches.
  3. Filter to exact name matches; sort by lastModified descending; take the first.
  4. If found: PUT /v2.1/accounts/{accountId}/templates/{templateId} (update).
  5. If not found: POST /v2.1/accounts/{accountId}/templates (create).
  6. Print Updated template {id} or Created template {id}.
  7. Add --force-create flag to bypass upsert and always create.

Tests (tests/test_upload_upsert.py):

  • test_creates_when_no_match — no existing templates; POST called.
  • test_updates_most_recent_when_match — two existing templates with same name; PUT called on newer one.
  • test_force_create_bypasses_upsert--force-create always POSTs.
  • test_partial_name_match_ignored — template name contains search term but isn't exact; still creates.

Phase 2 — FastAPI Backend Foundation

Goal: Runnable FastAPI app with health endpoint, env config, and session middleware.

Endpoints:

  • GET /health{"status": "ok", "version": "2.0"}

Tests (tests/test_api_health.py):

  • test_health_returns_200
  • test_health_response_shape

Phase 3 — Auth Endpoints

Goal: Users can connect to Adobe Sign and DocuSign from the browser; tokens stored in server-side session.

Adobe Sign (OAuth 2.0 Authorization Code):

  • GET /api/auth/adobe/start → redirect to Adobe Sign OAuth URL
  • GET /api/auth/adobe/callback?code=... → exchange code for tokens; store in session
  • GET /api/auth/adobe/disconnect → clear Adobe tokens from session

DocuSign (OAuth 2.0 Authorization Code — demo sandbox):

  • GET /api/auth/docusign/start → redirect to DocuSign OAuth URL
  • GET /api/auth/docusign/callback?code=... → exchange code for tokens; store in session
  • GET /api/auth/docusign/disconnect → clear DocuSign tokens from session

Status:

  • GET /api/auth/status{"adobe": true/false, "docusign": true/false}

Tests (tests/test_api_auth.py):

  • test_status_unauthenticated — both false on fresh session.
  • test_adobe_callback_stores_token — mock token exchange; session updated.
  • test_docusign_callback_stores_token — same for DocuSign.
  • test_disconnect_clears_token — after disconnect, status shows false.

Phase 4 — Template Listing API

Goal: Expose Adobe and DocuSign template lists; compute per-template migration status.

Endpoints:

  • GET /api/templates/adobe → list of Adobe Sign library documents
  • GET /api/templates/docusign → list of DocuSign templates
  • GET /api/templates/status → merged view: each Adobe template tagged as:
    • not_migrated — no DocuSign template with same name
    • migrated — at least one DocuSign template with exact name match
    • needs_update — Adobe template modified after the matched DocuSign template

Tests (tests/test_api_templates.py):

  • test_adobe_list_requires_auth — 401 if not authenticated.
  • test_adobe_list_returns_templates — mock Adobe API; correct shape.
  • test_docusign_list_returns_templates — mock DocuSign API.
  • test_status_not_migrated — Adobe template with no DS match → not_migrated.
  • test_status_migrated — name match exists → migrated.
  • test_status_needs_update — Adobe modified after DS template → needs_update.

Phase 5 — Migration API

Goal: Trigger migration of selected Adobe templates and retrieve history.

Endpoints:

  • POST /api/migrate — body: {"adobe_template_ids": ["id1", "id2"]}
    • Downloads each template, runs compose_docusign_template.py, uploads via upsert
    • Returns {"results": [{"adobe_id": "...", "docusign_id": "...", "status": "created|updated|failed", "error": null}]}
  • GET /api/migrate/history — reads migration-output/.history.json; returns past runs

History record schema:

{
  "timestamp": "2026-04-17T10:30:00Z",
  "adobe_template_name": "NDA",
  "adobe_template_id": "CBJ...",
  "docusign_template_id": "7dfd...",
  "action": "created|updated",
  "status": "success|failed",
  "error": null
}

Tests (tests/test_api_migrate.py):

  • test_migrate_requires_auth — 401 if not authenticated.
  • test_migrate_single_template_creates — mock Adobe download + DS upload (no existing); returns created.
  • test_migrate_single_template_updates — mock with existing DS template; returns updated.
  • test_migrate_records_history — after run, history file updated.
  • test_history_returns_past_runs — GET history returns written records.
  • test_migrate_handles_partial_failure — one template fails; others succeed; partial results returned.

Phase 6 — Frontend

Goal: Single-page app served at /; no build step.

Layout:

┌─────────────────────────────────────────────────────┐
│  Adobe Sign → DocuSign Migrator                [auth]│
├───────────────────────┬─────────────────────────────┤
│  Adobe Sign Templates │  DocuSign Templates         │
│  [connect]            │  [connect]                  │
│  ─────────────────    │  ─────────────────────────  │
│  ● NDA          [●]   │  ● NDA                      │
│  ○ Sales Agmt   [○]   │  ● David Tag Demo           │
│  ○ Rob Test     [○]   │                             │
│                       │                             │
│  [Migrate Selected]   │                             │
└───────────────────────┴─────────────────────────────┘

Status badges:

  • Green dot = Migrated
  • Yellow dot = Needs Update
  • Red dot = Not Migrated

Migrate flow:

  1. User checks templates to migrate.
  2. Clicks "Migrate Selected."
  3. Progress shown inline per template (spinner → check/error).
  4. History section at bottom shows past runs.

Phase 7 — End-to-End & Regression Tests

Goal: Ensure the full pipeline works together and existing behaviour doesn't regress.

tests/test_e2e.py:

  • test_full_migration_flow — using TestClient + respx mocks:
    1. Connect Adobe (mock OAuth callback)
    2. Connect DocuSign (mock OAuth callback)
    3. GET /api/templates/status → at least one not_migrated
    4. POST /api/migrate → status created
    5. GET /api/templates/status → now migrated
    6. POST /api/migrate again → status updated
    7. GET /api/migrate/history → two entries

tests/test_regression.py:

  • Runs compose_docusign_template.py on all fixtures in sample-templates/
  • Validates output against expected JSON snapshots in tests/fixtures/expected/
  • Any field type regression fails the test
  • Run via pytest tests/test_regression.py — no live API calls needed

Running Tests

# All tests
pytest tests/ -v

# Unit only (no live API)
pytest tests/ -v -m "not integration"

# Regression only
pytest tests/test_regression.py -v

# With coverage
pytest tests/ --cov=src --cov=web --cov-report=term-missing

Dependencies to Add

fastapi
uvicorn[standard]
starlette-sessions
itsdangerous
httpx
respx
pytest-asyncio
pytest-cov

Last updated: 2026-04-17