Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 64 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Resolve package version
id: package-version
run: |
PACKAGE_VERSION="$(node scripts/latest-package-version.js)"
echo "Resolved package: ${PACKAGE_VERSION}"
echo "package=${PACKAGE_VERSION}" >> "$GITHUB_OUTPUT"

- name: Install LibreOffice
run: |
sudo apt-get update
Expand Down Expand Up @@ -85,9 +92,29 @@ jobs:
SCRATCH_ORG_AUTH_URL=$(sf org display --verbose --json --target-org ci-test-org | jq -r '.result.sfdxAuthUrl')
echo "scratch_org_auth_url=$SCRATCH_ORG_AUTH_URL" >> $GITHUB_OUTPUT

# Deploy metadata to scratch org
echo "📤 Deploying metadata to scratch org..."
sf project deploy start --source-dir force-app --wait 10
# Install package metadata exactly as subscribers receive it.
echo "📦 Installing Docgen unlocked package: ${DOCGEN_PACKAGE_VERSION}"
sf package install \
--package "${DOCGEN_PACKAGE_VERSION}" \
--target-org ci-test-org \
--wait 20 \
--publish-wait 20 \
--apex-compile package \
--no-prompt

# Deploy only scratch-org supplemental metadata.
# Do not deploy force-app/unpackaged/default/connectedApps in CI.
echo "📤 Deploying supported-object custom metadata..."
sf project deploy start \
--source-dir force-app/unpackaged/default/customMetadata \
--target-org ci-test-org \
--wait 10

echo "📤 Deploying test metadata..."
sf project deploy start \
--source-dir force-app/test \
--target-org ci-test-org \
--wait 10

# Assign permission set to user
echo "🔐 Assigning Docgen_User permission set..."
Expand All @@ -96,6 +123,7 @@ jobs:
echo "✅ Scratch org created and configured successfully"
env:
DEVHUB_SFDX_AUTH_URL: ${{ secrets.SFDX_AUTH_URL }}
DOCGEN_PACKAGE_VERSION: ${{ steps.package-version.outputs.package }}
continue-on-error: true

- name: Run integration tests
Expand Down Expand Up @@ -208,6 +236,13 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Resolve package version
id: package-version
run: |
PACKAGE_VERSION="$(node scripts/latest-package-version.js)"
echo "Resolved package: ${PACKAGE_VERSION}"
echo "package=${PACKAGE_VERSION}" >> "$GITHUB_OUTPUT"

- name: Install Salesforce CLI
run: |
npm install -g @salesforce/cli
Expand All @@ -225,16 +260,38 @@ jobs:
run: |
sf org create scratch --definition-file config/project-scratch-def.json --alias ciorg --set-default --duration-days 1 --wait 10

- name: Deploy metadata to Scratch Org
- name: Install package in Scratch Org
if: env.SFDX_AUTH_URL != ''
env:
DOCGEN_PACKAGE_VERSION: ${{ steps.package-version.outputs.package }}
run: |
sf package install \
--package "${DOCGEN_PACKAGE_VERSION}" \
--target-org ciorg \
--wait 20 \
--publish-wait 20 \
--apex-compile package \
--no-prompt

- name: Deploy supplemental metadata to Scratch Org
if: env.SFDX_AUTH_URL != ''
run: |
sf project deploy start --source-dir force-app --wait 10
# Deploy custom metadata records and test-only metadata without connected apps.
sf project deploy start \
--source-dir force-app/unpackaged/default/customMetadata \
--target-org ciorg \
--wait 10

sf project deploy start \
--source-dir force-app/test \
--target-org ciorg \
--wait 10

- name: Run Apex tests with coverage
if: env.SFDX_AUTH_URL != ''
run: |
sf apex run test --test-level RunLocalTests --code-coverage --result-format json --output-dir coverage-apex --wait 10
sf apex run test --test-level RunLocalTests --code-coverage --result-format human --wait 10 > apex-test-results.txt
sf apex run test --test-level RunLocalTests --code-coverage --result-format json --output-dir coverage-apex --target-org ciorg --wait 10
sf apex run test --test-level RunLocalTests --code-coverage --result-format human --target-org ciorg --wait 10 > apex-test-results.txt
cat apex-test-results.txt

- name: Upload Apex test results
Expand Down
36 changes: 33 additions & 3 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Resolve package version
id: package-version
run: |
PACKAGE_VERSION="$(node scripts/latest-package-version.js)"
echo "Resolved package: ${PACKAGE_VERSION}"
echo "package=${PACKAGE_VERSION}" >> "$GITHUB_OUTPUT"

- name: Install Salesforce CLI
run: npm install -g @salesforce/cli

Expand Down Expand Up @@ -63,10 +70,33 @@ jobs:
exit 1
fi

- name: Deploy Salesforce metadata
- name: Install Docgen unlocked package
env:
DOCGEN_PACKAGE_VERSION: ${{ steps.package-version.outputs.package }}
run: |
sf package install \
--package "${DOCGEN_PACKAGE_VERSION}" \
--target-org ${{ steps.create-org.outputs.org_alias }} \
--wait 20 \
--publish-wait 20 \
--apex-compile package \
--no-prompt
echo "✅ Package installed"

- name: Deploy supplemental Salesforce metadata
run: |
sf project deploy start --source-dir force-app --wait 10
echo "✅ Metadata deployed"
# Deploy custom metadata records and test-only metadata without connected apps.
sf project deploy start \
--source-dir force-app/unpackaged/default/customMetadata \
--target-org ${{ steps.create-org.outputs.org_alias }} \
--wait 10

sf project deploy start \
--source-dir force-app/test \
--target-org ${{ steps.create-org.outputs.org_alias }} \
--wait 10

echo "✅ Supplemental metadata deployed"

- name: Configure External Credential with AAD secrets
id: apex-config
Expand Down
121 changes: 79 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io/)
[![Salesforce](https://img.shields.io/badge/Salesforce-Integration-00A1E0?logo=salesforce)](https://www.salesforce.com/)
[![Salesforce Package](https://img.shields.io/badge/Package-v0.3.1-00A1E0?logo=salesforce)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tWS000001jy7XYAQ)

A production-ready document generation service that creates PDF documents from Salesforce data using DOCX templates and LibreOffice, deployed on Azure Container Apps.

Expand Down Expand Up @@ -162,7 +163,6 @@ sequenceDiagram
end
```


## Key Features

- **Dual Processing Modes**: Interactive (synchronous) and batch (asynchronous) generation
Expand Down Expand Up @@ -201,7 +201,36 @@ npm test
npm run dev
```

### Salesforce Setup
### Salesforce Package Installation

The Salesforce components are available as an **unlocked package** for easy installation:

Package: 0HoWS00000009uX0AQ - Processity PBO

[![Install Unlocked Package](https://img.shields.io/badge/Install-Unlocked%20Package-00A1E0?logo=salesforce)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tWS000001jy7XYAQ)

**Option 1: Install via URL**

Click the badge above or use this link:

```
https://login.salesforce.com/packaging/installPackage.apexp?p0=04tWS000001jy7XYAQ
```

**Option 2: Deploy metadata to org**

```bash
# Deploy metadata to org
sf project deploy --target-org <YourOrg> --wait 30 --no-prompt
```

**After Installation:**

1. Assign the `Docgen_User` permission set to users who need access
2. Configure the Named Credential endpoint URL for your Node.js API
3. Set up External Credential principals for Azure AD authentication

### Salesforce Setup (Development)

```bash
# Authenticate to Dev Hub
Expand Down Expand Up @@ -233,36 +262,36 @@ For detailed setup instructions, see [Quick Start Guide](docs/quick-start.md).

### For Developers

| Document | Description |
|----------|-------------|
| **[Quick Start Guide](docs/quick-start.md)** | Complete setup and installation guide for new developers |
| **[Scripts Reference](docs/scripts.md)** | Detailed documentation for all helper scripts and Apex templates |
| **[Architecture Guide](docs/architecture.md)** | Technical implementation details (authentication, caching, conversion, batch processing, composite documents) |
| **[Testing Guide](docs/testing.md)** | Running tests (Node.js, Apex, LWC, E2E) and CI/CD configuration |
| **[API Reference](docs/api.md)** | REST API endpoints, request/response formats, error handling, composite envelope format |
| **[Template Authoring](docs/template-authoring.md)** | Creating DOCX templates with merge fields, loops, conditionals, and composite namespaces |
| **[Field Path Conventions](docs/field-path-conventions.md)** | Data structure and field path syntax including namespace-scoped paths |
| **[ADRs](docs/adr/)** | Architecture Decision Records (runtime, auth, worker, caching) |
| Document | Description |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| **[Quick Start Guide](docs/quick-start.md)** | Complete setup and installation guide for new developers |
| **[Scripts Reference](docs/scripts.md)** | Detailed documentation for all helper scripts and Apex templates |
| **[Architecture Guide](docs/architecture.md)** | Technical implementation details (authentication, caching, conversion, batch processing, composite documents) |
| **[Testing Guide](docs/testing.md)** | Running tests (Node.js, Apex, LWC, E2E) and CI/CD configuration |
| **[API Reference](docs/api.md)** | REST API endpoints, request/response formats, error handling, composite envelope format |
| **[Template Authoring](docs/template-authoring.md)** | Creating DOCX templates with merge fields, loops, conditionals, and composite namespaces |
| **[Field Path Conventions](docs/field-path-conventions.md)** | Data structure and field path syntax including namespace-scoped paths |
| **[ADRs](docs/adr/)** | Architecture Decision Records (runtime, auth, worker, caching) |

### For Operations

| Document | Description |
|----------|-------------|
| **[Deployment Guide](docs/deploy.md)** | CI/CD workflows, deployment procedures, rollback strategies |
| **[Provisioning Guide](docs/provisioning.md)** | One-time environment setup in Azure |
| **[Runbooks](docs/runbooks.md)** | Operational procedures (scaling, key rotation, disaster recovery) |
| **[Monitoring & Dashboards](docs/dashboards.md)** | Application Insights dashboards, KQL queries, alert rules |
| **[Troubleshooting Index](docs/troubleshooting-index.md)** | Common issues and resolution steps |
| Document | Description |
| ---------------------------------------------------------- | ----------------------------------------------------------------- |
| **[Deployment Guide](docs/deploy.md)** | CI/CD workflows, deployment procedures, rollback strategies |
| **[Provisioning Guide](docs/provisioning.md)** | One-time environment setup in Azure |
| **[Runbooks](docs/runbooks.md)** | Operational procedures (scaling, key rotation, disaster recovery) |
| **[Monitoring & Dashboards](docs/dashboards.md)** | Application Insights dashboards, KQL queries, alert rules |
| **[Troubleshooting Index](docs/troubleshooting-index.md)** | Common issues and resolution steps |

### For Administrators

| Document | Description |
|----------|-------------|
| **[Admin Guide](docs/admin-guide.md)** | Salesforce admin setup, adding support for new objects, creating composite documents |
| **[Admin Runbook](docs/admin-runbook.md)** | Administrative operations and troubleshooting |
| **[Named Credential Setup](docs/named-credential-setup.md)** | Configuring Azure AD authentication from Salesforce |
| **[LWC Composite Button Guide](docs/lwc-composite-button-guide.md)** | Configuring compositeDocgenButton component on Lightning pages |
| **[Composite Batch Examples](docs/composite-batch-examples.md)** | Batch generation patterns for composite documents |
| Document | Description |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| **[Admin Guide](docs/admin-guide.md)** | Salesforce admin setup, adding support for new objects, creating composite documents |
| **[Admin Runbook](docs/admin-runbook.md)** | Administrative operations and troubleshooting |
| **[Named Credential Setup](docs/named-credential-setup.md)** | Configuring Azure AD authentication from Salesforce |
| **[LWC Composite Button Guide](docs/lwc-composite-button-guide.md)** | Configuring compositeDocgenButton component on Lightning pages |
| **[Composite Batch Examples](docs/composite-batch-examples.md)** | Batch generation patterns for composite documents |

## Project Structure

Expand Down Expand Up @@ -292,13 +321,15 @@ docgen/
## Salesforce Components

### Custom Objects
- **Docgen_Template__c**: Template configuration (links to ContentVersion)
- **Generated_Document__c**: Document generation tracking and status
- **Composite_Document__c**: Multi-source document configuration
- **Composite_Document_Template__c**: Junction records linking composites to templates with namespaces
- **Supported_Object__mdt**: Multi-object configuration (Custom Metadata)

- **Docgen_Template\_\_c**: Template configuration (links to ContentVersion)
- **Generated_Document\_\_c**: Document generation tracking and status
- **Composite_Document\_\_c**: Multi-source document configuration
- **Composite_Document_Template\_\_c**: Junction records linking composites to templates with namespaces
- **Supported_Object\_\_mdt**: Multi-object configuration (Custom Metadata)

### Apex Classes

- **DocgenController**: Interactive generation controller for LWC (including composite generation)
- **DocgenEnvelopeService**: Request envelope builder with SHA-256 hashing (single and composite)
- **StandardSOQLProvider**: Data collection with locale-aware formatting
Expand All @@ -308,41 +339,47 @@ docgen/
**Test Coverage**: 112 Apex tests with 86% code coverage

### Lightning Web Components

- **docgenButton**: Single-template document generation button (deployable to any record page)
- **compositeDocgenButton**: Composite document generation button with recordIds mapping
- **docgenTestPage**: E2E testing wrapper component

### Custom App

The **Docgen** app includes:

- Docgen Templates tab (manage single-object templates)
- Composite Documents tab (manage multi-source templates)
- Generated Documents tab (track generation history for single and composite)
- Docgen Test Page tab (E2E testing interface)

## Technology Stack

| Layer | Technology |
|-------|------------|
| **Runtime** | Node.js 20+ with TypeScript |
| **Web Framework** | Fastify |
| **Template Engine** | docx-templates |
| **PDF Conversion** | LibreOffice (headless) |
| **Authentication** | Azure AD OAuth2 (inbound), Salesforce JWT Bearer (outbound) |
| **Testing** | Jest, Supertest, Nock, Playwright |
| **Infrastructure** | Azure Container Apps, Azure Container Registry, Azure Key Vault |
| **Observability** | Azure Application Insights, OpenTelemetry |
| **CI/CD** | GitHub Actions |
| Layer | Technology |
| ------------------- | --------------------------------------------------------------- |
| **Runtime** | Node.js 20+ with TypeScript |
| **Web Framework** | Fastify |
| **Template Engine** | docx-templates |
| **PDF Conversion** | LibreOffice (headless) |
| **Authentication** | Azure AD OAuth2 (inbound), Salesforce JWT Bearer (outbound) |
| **Testing** | Jest, Supertest, Nock, Playwright |
| **Infrastructure** | Azure Container Apps, Azure Container Registry, Azure Key Vault |
| **Observability** | Azure Application Insights, OpenTelemetry |
| **CI/CD** | GitHub Actions |

## API Endpoints

### Health & Readiness

- **GET /healthz**: Liveness probe (always returns 200)
- **GET /readyz**: Readiness probe with dependency checks

### Document Generation

- **POST /generate**: Generate PDF/DOCX from template (requires Azure AD token)

### Worker Management

- **POST /worker/start**: Start batch poller
- **POST /worker/stop**: Stop batch poller gracefully
- **GET /worker/status**: Current worker state
Expand Down
Loading
Loading