Files
Peter.Morton 9286ee8927 feat: Add X-Verint-KAB-Original-URL header to document exports
Adds HTTP response header containing original Google Drive URL
for exported documents to enable content traceability and auditing.

- Adds X-Verint-KAB-Original-URL header to successful export responses
- Header format: https://drive.google.com/file/d/{fileId}
- Present for all export formats (PDF, DOCX, plain text)
- Header omitted on error responses (4xx/5xx)
- 18 new tests (9 contract + 9 integration)
- Zero new dependencies
- Performance: 0.000019ms overhead per request

Implements:
- FR-001: Header present on successful exports (200 OK)
- FR-002: Header absent on error responses
- FR-003: Standard header name X-Verint-KAB-Original-URL
- FR-004: Standard URL format with file ID
- FR-005: Uses validated document.id from Google Drive API
- FR-006: Header present regardless of file accessibility
- FR-007: Consistent across all export formats
- FR-008: Minimal performance impact (< 5ms requirement)

Testing:
- Contract tests validate header presence, format, and error handling
- Integration tests verify behavior across formats and permissions
- All 18 tests passing
- 100% requirements coverage

Documentation:
- Feature specification (specs/001-gdrive-url-header/spec.md)
- Implementation plan (plan.md)
- Technical research (research.md)
- Data model (data-model.md)
- API contract (contracts/response-headers.md)
- User guide (quickstart.md)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 16:04:54 -05:00

477 lines
13 KiB
Markdown

# Quick Start: Google Drive Original URL Header
**Feature**: 001-gdrive-url-header
**Date**: 2026-03-27
**Status**: Draft
## Overview
This guide shows how to use the `X-Verint-KAB-Original-URL` HTTP response header that provides the original Google Drive URL for exported documents.
---
## What This Feature Adds
When you export a document from the Google Drive Content Adapter, the HTTP response now includes a header that tells you where the original document lives in Google Drive.
**Before (without this feature)**:
```http
HTTP/1.1 200 OK
Content-Type: application/pdf
X-Request-Id: req_550e8400-e29b-41d4-a716-446655440000
Content-Disposition: inline; filename="Document.pdf"
[PDF content]
```
**After (with this feature)**:
```http
HTTP/1.1 200 OK
Content-Type: application/pdf
X-Request-Id: req_550e8400-e29b-41d4-a716-446655440000
Content-Disposition: inline; filename="Document.pdf"
X-Verint-KAB-Original-URL: https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
[PDF content]
```
---
## Basic Usage
### 1. Export a Document
**Request**:
```bash
curl -I http://localhost:3000/documents/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
```
**Response Headers**:
```http
HTTP/1.1 200 OK
Content-Type: application/pdf
X-Request-Id: req_550e8400-e29b-41d4-a716-446655440000
Content-Disposition: inline; filename="Financial-Report.pdf"
Content-Length: 245760
X-Verint-KAB-Original-URL: https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
```
### 2. Extract the Google Drive URL
**cURL**:
```bash
curl -I http://localhost:3000/documents/YOUR_DOCUMENT_ID \
| grep -i x-verint-kab-original-url \
| cut -d' ' -f2
```
**Output**:
```
https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
```
### 3. Open the Original Document
Copy the URL from the header and paste it into your browser to view the original document in Google Drive.
---
## Use Cases
### Use Case 1: Audit Trail
Track where exported content came from for compliance and auditing:
```bash
#!/bin/bash
# Export document and log the source URL
DOCUMENT_ID="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
RESPONSE=$(curl -I http://localhost:3000/documents/$DOCUMENT_ID)
# Extract headers
REQUEST_ID=$(echo "$RESPONSE" | grep -i x-request-id | cut -d' ' -f2 | tr -d '\r')
SOURCE_URL=$(echo "$RESPONSE" | grep -i x-verint-kab-original-url | cut -d' ' -f2 | tr -d '\r')
# Log the export
echo "$(date -Iseconds): Exported $DOCUMENT_ID from $SOURCE_URL (Request: $REQUEST_ID)" >> export.log
```
**Example Log Output**:
```
2026-03-27T14:30:00-05:00: Exported 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms from https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms (Request: req_550e8400-e29b-41d4-a716-446655440000)
```
---
### Use Case 2: Content Verification
Verify that the exported content matches the source document:
```python
import requests
# Export document
document_id = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
response = requests.get(f"http://localhost:3000/documents/{document_id}")
if response.status_code == 200:
source_url = response.headers.get('X-Verint-KAB-Original-URL')
print(f"✓ Export successful")
print(f"✓ Source: {source_url}")
print(f"✓ Size: {len(response.content)} bytes")
print(f"\nTo verify content, open: {source_url}")
else:
print(f"✗ Export failed: {response.status_code}")
```
**Output**:
```
✓ Export successful
✓ Source: https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
✓ Size: 245760 bytes
To verify content, open: https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
```
---
### Use Case 3: Batch Export with Metadata
Export multiple documents and create a metadata file with source URLs:
```javascript
const fetch = require('node-fetch');
const fs = require('fs');
async function exportWithMetadata(documentIds) {
const results = [];
for (const docId of documentIds) {
const response = await fetch(`http://localhost:3000/documents/${docId}`);
if (response.ok) {
const content = await response.buffer();
const sourceUrl = response.headers.get('x-verint-kab-original-url');
const filename = response.headers.get('content-disposition')
.match(/filename="(.+)"/)[1];
// Save exported file
fs.writeFileSync(filename, content);
// Track metadata
results.push({
documentId: docId,
filename: filename,
sourceUrl: sourceUrl,
exportedAt: new Date().toISOString()
});
console.log(`✓ Exported ${filename}`);
} else {
console.log(`✗ Failed to export ${docId}: ${response.status}`);
}
}
// Save metadata
fs.writeFileSync('export-metadata.json', JSON.stringify(results, null, 2));
console.log(`\n✓ Saved metadata to export-metadata.json`);
}
// Export multiple documents
const docs = [
'1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
'2CyjMWt1YSB6oGNetLcEaCkhnVVsruqmct85PhzF3vqnt',
'3DzkNXu2ZTC7pHOfuMdFbDliowWtsvrndu96QiaG4wrO'
];
exportWithMetadata(docs);
```
**Output (export-metadata.json)**:
```json
[
{
"documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
"filename": "Financial-Report.pdf",
"sourceUrl": "https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
"exportedAt": "2026-03-27T19:30:00.000Z"
},
{
"documentId": "2CyjMWt1YSB6oGNetLcEaCkhnVVsruqmct85PhzF3vqnt",
"filename": "Meeting-Notes.docx",
"sourceUrl": "https://drive.google.com/file/d/2CyjMWt1YSB6oGNetLcEaCkhnVVsruqmct85PhzF3vqnt",
"exportedAt": "2026-03-27T19:30:05.000Z"
},
{
"documentId": "3DzkNXu2ZTC7pHOfuMdFbDliowWtsvrndu96QiaG4wrO",
"filename": "README.txt",
"sourceUrl": "https://drive.google.com/file/d/3DzkNXu2ZTC7pHOfuMdFbDliowWtsvrndu96QiaG4wrO",
"exportedAt": "2026-03-27T19:30:10.000Z"
}
]
```
---
### Use Case 4: User Interface Integration
Add a "View in Google Drive" link in your application:
```javascript
async function exportWithViewLink(documentId) {
const response = await fetch(`http://localhost:3000/documents/${documentId}`);
if (response.ok) {
const blob = await response.blob();
const sourceUrl = response.headers.get('x-verint-kab-original-url');
// Create download link for exported file
const downloadUrl = URL.createObjectURL(blob);
const downloadLink = document.createElement('a');
downloadLink.href = downloadUrl;
downloadLink.download = 'document.pdf';
downloadLink.textContent = 'Download Exported PDF';
// Create link to view original in Google Drive
const viewLink = document.createElement('a');
viewLink.href = sourceUrl;
viewLink.target = '_blank';
viewLink.textContent = 'View Original in Google Drive';
viewLink.className = 'view-original-link';
// Add to page
document.body.appendChild(downloadLink);
document.body.appendChild(viewLink);
}
}
```
---
## Client Libraries
### JavaScript/Node.js
```javascript
const fetch = require('node-fetch');
async function exportDocument(documentId) {
const response = await fetch(`http://localhost:3000/documents/${documentId}`);
const result = {
success: response.ok,
status: response.status,
content: response.ok ? await response.buffer() : null,
sourceUrl: response.headers.get('x-verint-kab-original-url'),
requestId: response.headers.get('x-request-id')
};
return result;
}
// Usage
const doc = await exportDocument('1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms');
console.log(`Source: ${doc.sourceUrl}`);
```
### Python
```python
import requests
def export_document(document_id):
url = f"http://localhost:3000/documents/{document_id}"
response = requests.get(url)
return {
'success': response.ok,
'status': response.status_code,
'content': response.content if response.ok else None,
'source_url': response.headers.get('X-Verint-KAB-Original-URL'),
'request_id': response.headers.get('X-Request-Id')
}
# Usage
doc = export_document('1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms')
print(f"Source: {doc['source_url']}")
```
### cURL
```bash
# Export and extract source URL
export_document() {
local doc_id="$1"
local output_file="$2"
# Download document and save headers
curl -D headers.txt -o "$output_file" \
"http://localhost:3000/documents/$doc_id"
# Extract and display source URL
local source_url=$(grep -i x-verint-kab-original-url headers.txt | cut -d' ' -f2 | tr -d '\r')
echo "Downloaded: $output_file"
echo "Source: $source_url"
rm headers.txt
}
# Usage
export_document "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms" "report.pdf"
```
---
## Error Handling
### When Header is Missing
The header is **only present on successful exports** (200 OK). Error responses do not include the header.
**Example**:
```bash
# Document not found
curl -I http://localhost:3000/documents/INVALID_ID
```
**Response**:
```http
HTTP/1.1 404 Not Found
X-Request-Id: req_660f9511-f3ac-52e5-b827-557766551111
Document not found
```
**Note**: No `X-Verint-KAB-Original-URL` header in the response.
### Checking for Header Presence
**JavaScript**:
```javascript
const response = await fetch(`http://localhost:3000/documents/${docId}`);
if (response.ok) {
const sourceUrl = response.headers.get('x-verint-kab-original-url');
if (sourceUrl) {
console.log(`Export successful. Source: ${sourceUrl}`);
} else {
console.warn('Export succeeded but no source URL available');
}
} else {
console.error(`Export failed: ${response.status}`);
// Header will not be present on error responses
}
```
**Python**:
```python
response = requests.get(f"http://localhost:3000/documents/{doc_id}")
if response.ok:
source_url = response.headers.get('X-Verint-KAB-Original-URL')
if source_url:
print(f"Export successful. Source: {source_url}")
else:
print("Export succeeded but no source URL available")
else:
print(f"Export failed: {response.status_code}")
# Header will not be present on error responses
```
---
## FAQ
### Q: Why is the header missing on error responses?
**A**: The header is only included when the export succeeds (200 OK). If the export fails (404, 401, 403, 413, 5xx), there's no valid document to link to, so the header is omitted.
### Q: Can I trust the URL to be valid?
**A**: Yes. The URL is constructed from the document ID that Google Drive itself returned, so it's guaranteed to be a valid Google Drive URL. However, you may still need appropriate permissions to access the document in Google Drive.
### Q: What if I don't have access to the document in Google Drive?
**A**: The URL will still be present in the header, but opening it may prompt you to request access or show a "permission denied" error in Google Drive. The adapter exports content using a service account that has access; your personal Google account may not have the same permissions.
### Q: Does the header work for all export formats?
**A**: Yes. The header is present on all successful exports regardless of format (PDF, DOCX, plain text, etc.).
### Q: Can I use this for tracking and analytics?
**A**: Absolutely. The header is designed for exactly this purpose - tracking content origins, building audit trails, and providing attribution.
---
## Testing
### Verify Header Presence
```bash
# Test that header is present on success
curl -I http://localhost:3000/documents/VALID_DOCUMENT_ID \
| grep -q "X-Verint-KAB-Original-URL" \
&& echo "✓ Header present" \
|| echo "✗ Header missing"
# Test that header is absent on error
curl -I http://localhost:3000/documents/INVALID_ID \
| grep -q "X-Verint-KAB-Original-URL" \
&& echo "✗ Header present (should be absent)" \
|| echo "✓ Header correctly absent"
```
### Verify URL Format
```bash
# Extract URL and verify format
URL=$(curl -I http://localhost:3000/documents/VALID_DOCUMENT_ID \
| grep -i x-verint-kab-original-url \
| cut -d' ' -f2 \
| tr -d '\r')
if [[ $URL =~ ^https://drive\.google\.com/file/d/[a-zA-Z0-9_-]+$ ]]; then
echo "✓ URL format is valid: $URL"
else
echo "✗ URL format is invalid: $URL"
fi
```
---
## Next Steps
1. **Update your client code** to extract and use the `X-Verint-KAB-Original-URL` header
2. **Add audit logging** to track content origins
3. **Build UI features** that link back to original documents
4. **Test with your specific document IDs** to ensure compatibility
---
## References
- **Feature Specification**: [spec.md](./spec.md)
- **API Contract**: [contracts/response-headers.md](./contracts/response-headers.md)
- **Data Model**: [data-model.md](./data-model.md)
- **Implementation Plan**: [plan.md](./plan.md)
---
## Support
If you encounter issues:
1. Check that you're using a valid document ID
2. Verify the adapter is running and accessible
3. Confirm the document exists in Google Drive
4. Check the service account has access to the document
5. Review error logs for the request ID (in `X-Request-Id` header)
For questions about the feature specification, see [spec.md](./spec.md).