Initial commit: SAP C4C attachment downloader toolkit
- Add Python script for downloading C4C attachments via OData and web scraping - Add Java wrapper for programmatic access with typed API - Add DSM upload utility for Synology NAS integration - Add CLAUDE.md documentation for future development - Add .gitignore for Python, Java, and sensitive files
This commit is contained in:
187
CLAUDE.md
Normal file
187
CLAUDE.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a SAP C4C (Cloud for Customer) attachment downloader toolkit that retrieves attachments from ServiceRequest tickets and optionally uploads them to Synology DSM NAS. The project consists of:
|
||||
|
||||
- **Python script** (`sap-c4c-AttachmentFolder.py`): Core downloader using OData APIs and web scraping
|
||||
- **Java wrapper** (`C4CAttachmentDownloader.java`): Java interface that calls the Python script via ProcessBuilder
|
||||
- **DSM upload script** (`dsm-upload.py`): Standalone Synology NAS upload utility
|
||||
|
||||
## Architecture
|
||||
|
||||
### Python Script (`sap-c4c-AttachmentFolder.py`)
|
||||
|
||||
**Core functionality:**
|
||||
1. Authenticates to SAP C4C using Basic Auth
|
||||
2. Fetches ServiceRequest attachments via OData endpoints:
|
||||
- `/sap/c4c/odata/v1/c4codata` - Standard C4C OData API
|
||||
- `/sap/c4c/odata/cust/v1/custticketapi` - Custom ticket API
|
||||
3. Downloads two types of attachments:
|
||||
- **File attachments** (CategoryCode=2): Downloaded via OData `$value` endpoint
|
||||
- **Link attachments** (CategoryCode=3): External Salesforce links scraped using Scrapling + Playwright
|
||||
4. Handles XIssueItem-level attachments via `BO_XSRIssueItemAttachmentFolder`
|
||||
5. Optionally uploads downloaded files to Synology DSM via FileStation API
|
||||
|
||||
**Key dependencies:**
|
||||
- `requests` - HTTP client for OData/REST APIs
|
||||
- `scrapling[all]` - Web scraping framework with stealth capabilities
|
||||
- `playwright` - Browser automation for downloading Salesforce attachments
|
||||
|
||||
**Output modes:**
|
||||
- Human-readable console output (default)
|
||||
- JSON mode (`--json`) for programmatic consumption
|
||||
|
||||
### Java Wrapper (`C4CAttachmentDownloader.java`)
|
||||
|
||||
Provides a type-safe Java API that:
|
||||
- Invokes the Python script via `ProcessBuilder`
|
||||
- Passes credentials via environment variables (more secure than CLI args)
|
||||
- Parses JSON output into strongly-typed Java objects
|
||||
- Supports timeout configuration (default: 30 minutes)
|
||||
|
||||
**Key classes:**
|
||||
- `Result` - Top-level response containing all attachment metadata
|
||||
- `Attachment` - Individual attachment metadata (UUID, filename, MIME type, category)
|
||||
- `IssueItem` - XIssueItem with nested attachments
|
||||
- `DownloadedFile` - Download result with local path and error info
|
||||
- `DsmUploadEntry` - DSM upload result per file
|
||||
|
||||
### DSM Upload (`dsm-upload.py`)
|
||||
|
||||
Standalone script demonstrating Synology FileStation API usage:
|
||||
1. Login via `SYNO.API.Auth` to obtain SID
|
||||
2. Upload files via `SYNO.FileStation.Upload` with SID cookie
|
||||
|
||||
## Common Commands
|
||||
|
||||
### Python Script
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install requests scrapling[all] playwright
|
||||
python -m playwright install chromium
|
||||
|
||||
# Download attachments (credentials via CLI)
|
||||
python sap-c4c-AttachmentFolder.py \
|
||||
--tenant https://xxx.c4c.saphybriscloud.cn \
|
||||
--user admin \
|
||||
--password xxx \
|
||||
--ticket 24588
|
||||
|
||||
# Download with DSM upload
|
||||
python sap-c4c-AttachmentFolder.py \
|
||||
--tenant https://xxx.c4c.saphybriscloud.cn \
|
||||
--user admin \
|
||||
--password xxx \
|
||||
--ticket 24588 \
|
||||
--dsm-url http://10.0.10.235:5000 \
|
||||
--dsm-user PLM \
|
||||
--dsm-password 123456 \
|
||||
--dsm-path /Newgonow/AU-SPFJ
|
||||
|
||||
# JSON mode (for Java/programmatic use)
|
||||
python sap-c4c-AttachmentFolder.py --ticket 24588 --json
|
||||
|
||||
# List attachments only (no download)
|
||||
python sap-c4c-AttachmentFolder.py --ticket 24588 --list-only
|
||||
|
||||
# Using environment variables for credentials
|
||||
export C4C_TENANT=https://xxx.c4c.saphybriscloud.cn
|
||||
export C4C_USERNAME=admin
|
||||
export C4C_PASSWORD=xxx
|
||||
export DSM_URL=http://10.0.10.235:5000
|
||||
export DSM_USERNAME=PLM
|
||||
export DSM_PASSWORD=123456
|
||||
export DSM_PATH=/Newgonow/AU-SPFJ
|
||||
python sap-c4c-AttachmentFolder.py --ticket 24588 --json
|
||||
```
|
||||
|
||||
### Java Wrapper
|
||||
|
||||
```java
|
||||
// Compile (requires Jackson for JSON parsing)
|
||||
javac -cp jackson-databind.jar:jackson-core.jar:jackson-annotations.jar C4CAttachmentDownloader.java
|
||||
|
||||
// Basic usage
|
||||
C4CAttachmentDownloader downloader = new C4CAttachmentDownloader(
|
||||
"/path/to/sap-c4c-AttachmentFolder.py",
|
||||
"https://xxx.c4c.saphybriscloud.cn",
|
||||
"admin",
|
||||
"password"
|
||||
);
|
||||
|
||||
// List attachments only
|
||||
C4CAttachmentDownloader.Result result = downloader.listAttachments("24588");
|
||||
|
||||
// Download to default directory
|
||||
C4CAttachmentDownloader.Result result = downloader.download("24588");
|
||||
|
||||
// Download to specific directory
|
||||
C4CAttachmentDownloader.Result result = downloader.download("24588", "/tmp/ticket_24588");
|
||||
|
||||
// Download with DSM upload
|
||||
downloader.setDsmConfig("http://10.0.10.235:5000", "PLM", "123456", "/Newgonow/AU-SPFJ");
|
||||
C4CAttachmentDownloader.Result result = downloader.download("24588", "/tmp/ticket_24588");
|
||||
```
|
||||
|
||||
## Key Implementation Details
|
||||
|
||||
### Attachment Categories
|
||||
|
||||
SAP C4C uses `CategoryCode` to distinguish attachment types:
|
||||
- **"2"** = File attachment (binary content stored in C4C, downloaded via OData `$value`)
|
||||
- **"3"** = Link attachment (external URL, typically Salesforce links requiring web scraping)
|
||||
|
||||
### OData Navigation Paths
|
||||
|
||||
**ServiceRequest attachments:**
|
||||
```
|
||||
/ServiceRequestCollection('{ObjectID}')/ServiceRequestAttachmentFolder
|
||||
```
|
||||
|
||||
**XIssueItem attachments (two-step navigation):**
|
||||
```
|
||||
1. /BO_XSRIssueItemAttachmentCollection?$filter=XIssueItemUUID eq guid'{uuid}'
|
||||
2. /BO_XSRIssueItemAttachmentCollection('{ObjectID}')/BO_XSRIssueItemAttachmentFolder
|
||||
```
|
||||
|
||||
### Scrapling Download Strategy
|
||||
|
||||
For CategoryCode=3 (link attachments), the script:
|
||||
1. Opens the Salesforce link in a headless Chromium browser
|
||||
2. Waits for `button.downloadbutton[title='Download']` selector
|
||||
3. Clicks the button and captures the download
|
||||
4. Saves with original or suggested filename
|
||||
|
||||
### Security Considerations
|
||||
|
||||
- Java wrapper passes credentials via **environment variables** (not CLI args) to avoid exposure in process lists
|
||||
- Python script supports both CLI args and environment variables
|
||||
- DSM API uses session-based authentication (SID cookie)
|
||||
- SSL verification disabled (`verify=False`) - consider enabling in production
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── C4CAttachmentDownloader.java # Java wrapper with typed API
|
||||
├── sap-c4c-AttachmentFolder.py # Core Python downloader
|
||||
├── dsm-upload.py # Standalone DSM upload example
|
||||
└── downloads/ # Default output directory
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Playwright not installed:**
|
||||
```bash
|
||||
python -m playwright install chromium
|
||||
```
|
||||
|
||||
**Timeout errors:** Increase timeout in Java wrapper constructor (default 30 minutes) or adjust Scrapling timeout parameters.
|
||||
|
||||
**DSM upload fails:** Verify DSM URL, credentials, and that target path exists or `create_parents=true` is set.
|
||||
|
||||
**Link download fails:** Check that Salesforce page structure matches expected selector (`button.downloadbutton[title='Download']`). Update `download_link_via_scrapling()` if page structure changes.
|
||||
Reference in New Issue
Block a user