mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-26 22:32:23 +02:00
387 lines
11 KiB
Markdown
387 lines
11 KiB
Markdown
# Huly Export Service
|
|
|
|
The Export Service provides asynchronous data export functionality for Huly workspaces. It allows workspace owners and administrators to export documents and their associated data in multiple formats (JSON, CSV, Unified).
|
|
|
|
## Overview
|
|
|
|
The Export Service is a standalone microservice that:
|
|
- Exports workspace data asynchronously via REST API
|
|
- Exports documents from one workspace to another workspace
|
|
- Converts Huly documents to standardized formats
|
|
- Handles complex document relationships (references, collections, attachments)
|
|
- Packages exports as ZIP archives and saves them to the workspace Drive
|
|
- Sends notifications upon completion or failure
|
|
|
|
## Architecture
|
|
|
|
### Components
|
|
|
|
- **Server** (`server.ts`): Express REST API server with authentication
|
|
- **Exporter** (`exporter.ts`): Main export orchestration logic for file exports
|
|
- **Workspace Exporter** (`workspace/`): Cross-workspace document export functionality
|
|
- `workspace-exporter.ts`: Main orchestrator for workspace-to-workspace exports
|
|
- `document-exporter.ts`: Individual document export logic
|
|
- `attachment-exporter.ts`: Attachment and blob migration
|
|
- `relation-exporter.ts`: Forward and inverse relation handling
|
|
- `space-exporter.ts`: Space migration and reuse
|
|
- `data-mapper.ts`: Data transformation and field mapping
|
|
- **Converter** (`converter.ts`): Document conversion and type resolution
|
|
- **Serializers**: Format-specific output writers
|
|
- `json/json-serializer.ts`: JSON format output
|
|
- `csv/csv-serializer.ts`: CSV format output
|
|
- **Types** (`types.ts`): Unified document model definitions
|
|
|
|
### Export Flow
|
|
|
|
```
|
|
Client Request → Authentication → Export Job Created → Async Processing
|
|
↓
|
|
Notification ← Save to Drive ← ZIP Archive ← Serialize ← Convert Docs
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
### POST `/exportAsync`
|
|
|
|
Initiates an asynchronous export job.
|
|
|
|
**Authentication**: Required (Bearer token or Cookie)
|
|
|
|
**Query Parameters**:
|
|
- `format` (required): Export format - `json`, `csv`, or `unified`
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"_class": "task:class:Issue",
|
|
"query": {
|
|
"status": "active"
|
|
},
|
|
"attributesOnly": false
|
|
}
|
|
```
|
|
|
|
**Parameters**:
|
|
- `_class` (required): Ref to the document class to export (e.g., `task:class:Issue`)
|
|
- `query` (optional): MongoDB-style query to filter documents
|
|
- `attributesOnly` (boolean): If true, excludes attachments and collections
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"message": "Export started"
|
|
}
|
|
```
|
|
|
|
**Authorization**:
|
|
- Requires workspace Owner role or system admin privileges
|
|
- Read-only tokens are rejected
|
|
|
|
**Process**:
|
|
1. Job is queued and response is returned immediately
|
|
2. Documents are fetched, converted, and serialized
|
|
3. Output is packaged as a ZIP archive
|
|
4. Archive is uploaded to workspace Drive
|
|
5. User receives notification with download link
|
|
|
|
### POST `/export-to-workspace`
|
|
|
|
Exports documents from the current workspace to another workspace. This endpoint copies documents, their attachments, and related data to a target workspace while preserving relationships and remapping IDs.
|
|
|
|
**Authentication**: Required (Bearer token or Cookie)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"targetWorkspace": "target-workspace-uuid",
|
|
"_class": "documents:class:ControlledDocument",
|
|
"query": {
|
|
"_id": { "$in": ["doc-id-1", "doc-id-2"] }
|
|
},
|
|
"conflictStrategy": "duplicate",
|
|
"includeAttachments": true,
|
|
"relations": [
|
|
{
|
|
"field": "references",
|
|
"class": "documents:class:ControlledDocument",
|
|
"direction": "forward"
|
|
}
|
|
],
|
|
"fieldMappers": {
|
|
"documents:class:ControlledDocument": {
|
|
"author": "$currentUser",
|
|
"owner": "$currentUser",
|
|
"state": "draft",
|
|
"code": "$ensureUnique",
|
|
"seqNumber": "$ensureUnique"
|
|
}
|
|
},
|
|
"objectId": "doc-id-1",
|
|
"objectSpace": "space-id"
|
|
}
|
|
```
|
|
|
|
**Parameters**:
|
|
- `targetWorkspace` (required): UUID of the target workspace to export to
|
|
- `_class` (required): Ref to the document class to export (e.g., `documents:class:ControlledDocument`)
|
|
- `query` (optional): MongoDB-style query to filter documents. If not provided and specific documents are selected, uses `_id: { $in: [...] }`
|
|
- `conflictStrategy` (optional): How to handle existing documents in target workspace
|
|
- `"duplicate"` (default): Create new documents with new IDs
|
|
- `"skip"`: Skip documents that already exist (based on matching criteria)
|
|
- `includeAttachments` (optional, default: `true`): Whether to copy attachment blob data
|
|
- `relations` (optional): Array of relation definitions to export
|
|
- `field`: Field name containing the relation
|
|
- `class`: Class of related documents
|
|
- `direction`: `"forward"` (dependencies) or `"inverse"` (references)
|
|
- `fieldMappers` (optional): Field value overrides per document class. Supports special values:
|
|
- `$currentUser`: Replaced with current account's employee ID
|
|
- Example: `{ "author": "$currentUser" }` sets author to current user
|
|
- `$ensureUnique`: Ensures the field value is unique by checking the database and modifying if needed
|
|
- For strings: appends a suffix like "-1", "-2", etc.
|
|
- For numbers: increments until unique
|
|
- Example: `{ "code": "$ensureUnique", "seqNumber": "$ensureUnique" }` ensures unique code and seqNumber
|
|
- `objectId` (optional): ID of the primary document for notification context
|
|
- `objectSpace` (optional): Space of the primary document for notification context
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"message": "Export started"
|
|
}
|
|
```
|
|
|
|
**Authorization**:
|
|
- Requires Owner role in **both** source and target workspaces (or system admin privileges)
|
|
- Read-only tokens are rejected
|
|
- Target workspace must be accessible to the user
|
|
|
|
**Process**:
|
|
1. Validates user permissions for both source and target workspaces
|
|
2. Response is returned immediately (async processing)
|
|
3. Documents are fetched from source workspace
|
|
4. Forward relations (dependencies) are exported first
|
|
5. Main documents are exported with ID remapping
|
|
6. Inverse relations (references) are exported
|
|
7. Attachments and blob data are copied to target workspace
|
|
8. Spaces are created or reused in target workspace
|
|
9. User receives notification with export results
|
|
|
|
**Example Use Cases**:
|
|
- Copying controlled documents to a new workspace
|
|
- Migrating project data between workspaces
|
|
- Creating workspace templates
|
|
- Sharing documents with another team
|
|
|
|
**Error Handling**:
|
|
- Individual document failures don't stop the entire export
|
|
- Errors are collected and reported in the notification
|
|
- Failed documents are logged with specific error messages
|
|
|
|
## Export Formats
|
|
|
|
### JSON Format
|
|
- Human-readable nested JSON structure
|
|
- Preserves document relationships
|
|
- Includes metadata and attachments
|
|
- File structure: `{spaceName}/{className}/{docId}.json`
|
|
|
|
### CSV Format
|
|
- Flat tabular format
|
|
- One row per document
|
|
- Nested objects converted to JSON strings
|
|
- File structure: `{spaceName}/{className}.csv`
|
|
|
|
### Unified Format
|
|
- Standardized document structure
|
|
- Generic type system
|
|
- Suitable for data migration/integration
|
|
- File structure: `{spaceName}/{className}/{docId}.json`
|
|
|
|
## Document Conversion
|
|
|
|
The converter transforms Huly documents into a unified format that:
|
|
|
|
### Handles Special Types
|
|
- **References** (`RefTo`): Resolved to readable format (e.g., person names)
|
|
- **Collections**: Nested child documents included
|
|
- **Timestamps**: Formatted as ISO 8601 strings
|
|
- **Markdown**: Preserved with field markers
|
|
- **Collaborative Docs**: Fetched from storage and included
|
|
- **Attachments**: Binary data handled separately
|
|
|
|
### Field Categories
|
|
Documents are analyzed and fields are categorized:
|
|
- `markdownFields`: Fields containing markdown content
|
|
- `collabFields`: Fields referencing collaborative documents
|
|
- `refFields`: Fields containing document references
|
|
- `collectionFields`: Fields containing nested collections
|
|
- `attachments`: Associated attachment metadata
|
|
|
|
### Example Unified Document
|
|
```json
|
|
{
|
|
"_class": "task:class:Issue",
|
|
"_id": "issue-123",
|
|
"space": "project-1",
|
|
"data": {
|
|
"title": "Fix bug",
|
|
"description": "...",
|
|
"assignee": "John Doe",
|
|
"status": "In Progress"
|
|
},
|
|
"markdownFields": ["description"],
|
|
"refFields": ["assignee", "status"],
|
|
"collectionFields": ["comments"],
|
|
"attachments": [
|
|
{
|
|
"id": "attach-1",
|
|
"name": "screenshot.png",
|
|
"size": 45678,
|
|
"contentType": "image/png"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Environment Variables
|
|
|
|
Required:
|
|
- `PORT`: Service port (default: 4006)
|
|
- `SECRET`: JWT token secret
|
|
- `ACCOUNTS_URL`: URL to accounts service
|
|
- `SERVICE_ID`: Service identifier
|
|
|
|
Storage configuration (via `storageConfigFromEnv`):
|
|
- `STORAGE_PROVIDER`: Storage backend (`minio`, `s3`, etc.)
|
|
- `STORAGE_ENDPOINT`: Storage endpoint URL
|
|
- `STORAGE_ACCESS_KEY`: Storage access credentials
|
|
- `STORAGE_SECRET_KEY`: Storage secret credentials
|
|
|
|
### Docker Deployment
|
|
|
|
```bash
|
|
docker build -t hardcoreeng/export .
|
|
docker run -p 4006:4006 \
|
|
-e SECRET=your-secret \
|
|
-e ACCOUNTS_URL=http://accounts:3000 \
|
|
-e SERVICE_ID=export-service \
|
|
-e STORAGE_PROVIDER=minio \
|
|
-e STORAGE_ENDPOINT=http://minio:9000 \
|
|
hardcoreeng/export
|
|
```
|
|
|
|
## Development
|
|
|
|
### Build
|
|
|
|
```bash
|
|
rush build --to @hcengineering/pod-export
|
|
```
|
|
|
|
### Run Locally
|
|
|
|
```bash
|
|
cd services/export/pod-export
|
|
ACCOUNTS_URL="http://127.0.0.1:3000" SECRET="secret" DB_URL=postgresql://root@huly.local:26257/defaultdb?sslmode=disable SERVICE_ID="export" STORAGE_CONFIG="datalake|http://huly.local:4030" rushx run-local
|
|
```
|
|
|
|
### Testing
|
|
|
|
```bash
|
|
rushx test
|
|
```
|
|
|
|
## Output Structure
|
|
|
|
```
|
|
export-{workspace}-{class}-{format}-{timestamp}.zip
|
|
├── Space Name 1/
|
|
│ ├── ClassName/
|
|
│ │ ├── doc-1.json
|
|
│ │ ├── doc-2.json
|
|
│ │ └── attachments/
|
|
│ │ ├── file1.pdf
|
|
│ │ └── file2.png
|
|
├── Space Name 2/
|
|
│ └── ClassName/
|
|
│ └── docs.csv
|
|
```
|
|
|
|
## Notifications
|
|
|
|
Users receive in-app notifications:
|
|
|
|
**Success**:
|
|
```
|
|
Export completed successfully
|
|
[Link to download from Drive]
|
|
```
|
|
|
|
**Failure**:
|
|
```
|
|
Export failed: {error message}
|
|
```
|
|
|
|
## Security
|
|
|
|
- JWT token validation
|
|
- Workspace access verification
|
|
- Role-based authorization (Owner role required for both source and target workspaces)
|
|
- Target workspace permission validation
|
|
- Secure file storage
|
|
- Temporary directory cleanup
|
|
- Read-only token rejection
|
|
|
|
## Limitations
|
|
|
|
- Maximum documents per export: Limited by available memory
|
|
- Attachment size: Limited by storage configuration
|
|
- Export timeout: 30 minutes (configurable)
|
|
- Concurrent exports per workspace: 1
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
**401 Unauthorized**
|
|
- Check token validity
|
|
- Verify workspace access
|
|
- Ensure user has Owner role
|
|
|
|
**400 Missing required parameters**
|
|
- Verify `_class` is provided
|
|
- Check `format` query parameter
|
|
|
|
**403 Forbidden**
|
|
- Read-only tokens cannot export
|
|
- Upgrade to full access token
|
|
- Verify Owner role in target workspace
|
|
- Check that target workspace is accessible
|
|
|
|
**404 Target workspace not found**
|
|
- Verify target workspace UUID is correct
|
|
- Ensure target workspace exists and is accessible
|
|
- Check user has access to target workspace
|
|
|
|
**Export fails silently**
|
|
- Check service logs
|
|
- Verify storage connectivity
|
|
- Ensure sufficient disk space
|
|
- Verify both source and target workspace connections are working
|
|
|
|
### Logs
|
|
|
|
```bash
|
|
docker logs -f export-service
|
|
```
|
|
|
|
## License
|
|
|
|
Eclipse Public License 2.0
|
|
|
|
## Support
|
|
|
|
For issues and questions, see the main Huly Platform repository.
|