You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+88Lines changed: 88 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,7 +30,9 @@ This plugin can be consumed by the CAP application deployed on BTP to store thei
30
30
-[Support for Edit of Link type attachments](#support-for-edit-of-link-type-attachments)
31
31
-[Support for Non-Draft Attachments](#support-for-non-draft-attachments)
32
32
-[Support for Multiple attachment facets](#support-for-multiple-attachment-facets)
33
+
-[Support for Large File Upload](#support-for-large-file-upload)
33
34
-[Support for Technical User](#support-for-technical-user)
35
+
-[Force Client Credentials Flow via Annotation](#force-client-credentials-flow-via-annotation)
34
36
-[Support for Multitenancy](#support-for-multitenancy)
35
37
-[Deploying and testing the application](#deploying-and-testing-the-application)
36
38
-[Running the unit tests](#running-the-unit-tests)
@@ -756,6 +758,46 @@ For row-press behavior in every facet table, configure each line item target:
756
758
}
757
759
```
758
760
761
+
## Support for Large File Upload
762
+
763
+
This plugin supports uploading files larger than 400 MB to SAP Document Management (SDM) without buffering the entire file in memory. The plugin automatically detects file size and routes the upload through either the single-POST path or a chunked path. Clients use the same OData `PUT .../content` request regardless of file size.
764
+
765
+
### Key Features
766
+
767
+
-**Automatic Routing**: Files ≤ 400 MB use the existing single-POST path; files > 400 MB use a chunked upload path
768
+
-**Streaming Upload**: Files > 400 MB are streamed in 20 MB chunks via CMIS `appendContentStream`, avoiding out-of-memory errors
769
+
-**Read-Ahead Buffering**: Up to 4 chunks (80 MB max) are pre-loaded while the previous chunk is uploading, improving throughput
770
+
-**Failure Recovery**: In-progress upload IDs are tracked in an orphan queue; incomplete documents are deleted with exponential-backoff retries on failure
771
+
-**Client Disconnect Handling**: Partial uploads are cleanly cleaned up if the OData client drops the connection mid-upload
772
+
-**Virus Scan Guard**: For repositories with virus scanning enabled, files > 400 MB are rejected upfront with HTTP 409 since SDM's virus scan service does not support files above this size
773
+
774
+
### How It Works
775
+
776
+
For attachment uploads via OData `PUT .../content`, the plugin automatically:
777
+
778
+
1.**Detects file size** from the HTTP `Content-Length` header before any data is streamed
779
+
2.**Routes small files (≤ 400 MB)** through the existing single-POST `createDocument` path — no change in behavior
780
+
3.**Routes large files (> 400 MB)** through the chunked path:
781
+
- Creates an empty placeholder document in SDM via `createDocument`
782
+
- Streams the file in 20 MB chunks via `appendContentStream`, with the last chunk marked `isLastChunk=true`
783
+
- Pre-loads up to 4 chunks in a read-ahead buffer while the previous chunk uploads
784
+
4.**Tracks orphans on failure**: if any chunk upload fails, the placeholder objectId is added to an orphan queue and the plugin attempts to delete the incomplete document with retry backoff
785
+
5.**Reconciles on restart**: any orphan queue entry that survived a previous failure is cleaned up by the startup reconciliation job
786
+
787
+
### Configuration
788
+
789
+
No client-side or CDS-side configuration is required. The thresholds are constants in the plugin:
790
+
791
+
| Constant | Value | Purpose |
792
+
|---|---|---|
793
+
|`FILE_SIZE_THRESHOLD`| 400 MB | Boundary between single-POST and chunked upload paths |
794
+
|`CHUNK_SIZE`| 20 MB | Size of each `appendContentStream` chunk |
795
+
796
+
### Virus Scan Repositories
797
+
798
+
SAP Document Management's virus-scan service does not support files above 400 MB. When `isVirusScanEnabled: true` is set on the SDM service binding, the plugin rejects uploads larger than 400 MB with HTTP 409 and a descriptive error message before any data is streamed, instead of letting the request fail later at the SDM side. Repositories without virus scanning are unaffected.
799
+
800
+
759
801
## Support for Technical User
760
802
The CAP OData operations can be performed on attachments using a technical user. This flow can be used for machine-to-machine (M2M) interactions, where user involvement is not necessary.
761
803
@@ -766,6 +808,52 @@ entity Incidents as projection on my.Incidents;
766
808
}
767
809
```
768
810
811
+
## Force Client Credentials Flow via Annotation
812
+
813
+
By default, the plugin uses the JWT-bearer flow when a user context is present in the incoming token (named-user authentication), and falls back to client-credentials only for technical users that have no user origin. Some scenarios — for example, customer requirements where end users do not have SDM roles but the application still needs to upload, rename, edit links, and update attachment metadata on their behalf — need the client-credentials flow regardless of whether the token carries a user context.
814
+
815
+
The `@SDM.useClientCredential: true` annotation on an attachments composition opts that composition into the client-credentials flow for all CRUD operations, irrespective of the calling user.
816
+
817
+
### Key Features
818
+
819
+
-**Per-Composition Scope**: A parent entity can mix flows — one attachment composition using client-credentials, another using the default JWT-bearer flow
820
+
-**Flow Override on All CRUD Paths**: Create, upload, rename, edit links, update metadata, and delete are all routed through the technical user when the annotation is set
821
+
-**Aligned `createdBy` / `modifiedBy`**: The plugin DB columns are stamped with the SDM client_id so the UI matches `cmis:createdBy` / `cmis:modifiedBy` recorded by DMS / DI
822
+
-**Default Preserved**: Without the annotation, existing behavior is unchanged — JWT-bearer when a user context is present, client-credentials only as a fallback
823
+
824
+
### How It Works
825
+
826
+
For an attachments composition annotated with `@SDM.useClientCredential: true`, the plugin:
827
+
828
+
1.**Detects the annotation** on the composition target via `req.target` for direct attachment operations, and via composition walking on parent SAVE events
829
+
2.**Authenticates every SDM call** with the SDM service binding's `clientid` / `clientsecret` (resolved from `VCAP_SERVICES`)
830
+
3.**Stamps `createdBy` / `modifiedBy`** with the same `clientid` on freshly activated draft rows so the plugin DB and the SDM backend show identical principals
831
+
832
+
### Entity Definition
833
+
834
+
The annotation must live on the attachments **target** (the composition target entity). In the sample Incidents app, the `footnotes` composition is annotated so footnote attachments are always uploaded under the technical user, while the human-user-authored `references` composition keeps the default flow:
835
+
836
+
```cds
837
+
using { sap.attachments.Attachments } from '@cap-js/sdm';
838
+
839
+
service ProcessorService {
840
+
entity Incidents as projection on my.Incidents;
841
+
}
842
+
843
+
// References — created by the human end-user (default flow)
844
+
extend my.Incidents with {
845
+
references : Composition of many Attachments;
846
+
footnotes : Composition of many Attachments;
847
+
}
848
+
849
+
// Footnotes — always stored under the SDM technical user
850
+
annotate my.Incidents.footnotes with @SDM.useClientCredential: true;
851
+
```
852
+
853
+
### Configuration
854
+
855
+
The SDM service binding must be available in `VCAP_SERVICES` so the plugin can resolve the client credentials. This is the normal binding setup; no extra configuration is required.
856
+
769
857
## Support for Multitenancy
770
858
771
859
This plugin automates repository lifecycle management in a multi-tenant setup. On tenant subscription, it provisions a repository and stores its details, and on unsubscription, it securely cleans up the repository.
0 commit comments