89 lines
4.2 KiB
Markdown
89 lines
4.2 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
SAP C4C (Cloud for Customer) attachment downloader toolkit that retrieves attachments from ServiceRequest tickets and optionally uploads them to Synology DSM NAS.
|
|
|
|
- **`sap-c4c-AttachmentFolder.py`**: Core downloader (Python >= 3.8) using OData APIs and web scraping
|
|
- **`C4CAttachmentDownloader.java`**: Java wrapper that calls the Python script via ProcessBuilder
|
|
- **`dsm-upload.py`**: Standalone Synology NAS upload example
|
|
|
|
## Common Commands
|
|
|
|
```bash
|
|
# Install dependencies
|
|
pip install requests scrapling[all] playwright
|
|
python -m playwright install chromium
|
|
|
|
# Download attachments
|
|
python sap-c4c-AttachmentFolder.py \
|
|
--tenant https://xxx.c4c.saphybriscloud.cn \
|
|
--user admin --password xxx --ticket 24588
|
|
|
|
# Download with custom concurrency (default: 5 threads)
|
|
python sap-c4c-AttachmentFolder.py --ticket 24588 --max-workers 10
|
|
|
|
# List attachments only (no download)
|
|
python sap-c4c-AttachmentFolder.py --ticket 24588 --list-only
|
|
|
|
# JSON mode (for Java/programmatic use)
|
|
python sap-c4c-AttachmentFolder.py --ticket 24588 --json
|
|
|
|
# Download + upload to Synology DSM
|
|
python sap-c4c-AttachmentFolder.py --ticket 24588 \
|
|
--dsm-url http://10.0.10.235:5000 --dsm-user PLM \
|
|
--dsm-password 123456 --dsm-path /Newgonow/AU-SPFJ
|
|
|
|
# All credentials also accept environment variables:
|
|
# C4C_TENANT, C4C_USERNAME, C4C_PASSWORD, DSM_URL, DSM_USERNAME, DSM_PASSWORD, DSM_PATH
|
|
```
|
|
|
|
```java
|
|
// Java: compile requires Jackson (jackson-databind, jackson-core, jackson-annotations)
|
|
javac -cp jackson-databind.jar:jackson-core.jar:jackson-annotations.jar C4CAttachmentDownloader.java
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Data Flow
|
|
|
|
1. Authenticate to SAP C4C via Basic Auth
|
|
2. Look up ServiceRequest by ticket ID -> get ObjectID and SerialID
|
|
3. Fetch SR-level attachments via `/sap/c4c/odata/v1/c4codata/ServiceRequestCollection('{OID}')/ServiceRequestAttachmentFolder`
|
|
4. Fetch XIssueItem-level attachments via `/sap/c4c/odata/cust/v1/custticketapi/BO_XSRIssueItemAttachmentCollection` (two-step: filter by UUID, then navigate to AttachmentFolder)
|
|
5. Download concurrently using ThreadPoolExecutor:
|
|
- **CategoryCode "2"** (file): OData `$value` endpoint or `DocumentLink` URL
|
|
- **CategoryCode "3"** (link): Scrapling + Playwright opens Salesforce URL, clicks `button.downloadbutton[title='Download']`, captures download
|
|
6. Optionally upload to Synology DSM via FileStation API, then **auto-delete local files**
|
|
|
|
### Two OData Endpoints
|
|
|
|
- `/sap/c4c/odata/v1/c4codata` (`ODATA_C4C`) - Standard C4C OData for ServiceRequest and SR-level attachments
|
|
- `/sap/c4c/odata/cust/v1/custticketapi` (`ODATA_CUST`) - Custom ticket API for XIssueItem and its attachments
|
|
|
|
### Java Wrapper
|
|
|
|
Invokes Python script with `--json` flag, passes credentials via **environment variables** (not CLI args for security). Parses JSON into typed classes: `Result`, `Attachment`, `IssueItem`, `DownloadedFile`, `DsmUploadEntry`. Default timeout: 30 minutes.
|
|
|
|
### DSM Upload Directory Structure
|
|
|
|
- SR attachments: `{DSM_PATH}/{ticketID}_{serialID}/{filename}`
|
|
- IssueItem attachments: `{DSM_PATH}/{ticketID}_{serialID}/{issueID}/{filename}`
|
|
|
|
### Concurrency Model
|
|
|
|
Multi-threaded via `ThreadPoolExecutor` (default 5, `--max-workers`). Both file and link downloads are submitted as futures. Thread-safe console output uses a `print_lock`. The `requests.Session` is shared across file-download threads (thread-safe). Scrapling/Playwright link downloads each launch their own browser.
|
|
|
|
### Global State
|
|
|
|
The Python script uses module-level globals (`TENANT`, `USERNAME`, `PASSWORD`, `ODATA_C4C`, `ODATA_CUST`, `OUTPUT_DIR`, `DSM_*`, `MAX_WORKERS`) initialized in `main()`. The `run()` function is the core entry point returning a structured dict.
|
|
|
|
## Troubleshooting
|
|
|
|
- **Playwright not installed**: `python -m playwright install chromium`
|
|
- **Link download fails**: Salesforce page selector `button.downloadbutton[title='Download']` may have changed; update `download_link_via_scrapling()`
|
|
- **Timeout**: Increase Java wrapper timeout or Scrapling's `timeout` param (currently 60s page load, 120s download wait)
|
|
- **SSL warnings**: `verify=False` is used throughout; `urllib3` warnings are suppressed
|