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>
477 lines
13 KiB
Markdown
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).
|