Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
74d682f
Changes for CreateAttachmentHandler
PujaDeshmukh17 Mar 5, 2025
27fb5ed
Code for updateAttachmentHandler
PujaDeshmukh17 Mar 5, 2025
cd6ae94
Refactored helper methods to util class
PujaDeshmukh17 Mar 5, 2025
a439d43
Refactor
PujaDeshmukh17 Mar 5, 2025
72e5c63
Adding new parameter to rename method
PujaDeshmukh17 Mar 6, 2025
3e0490c
Correcting tests in SDMCreateAttachmentHandlerTest
PujaDeshmukh17 Mar 6, 2025
84c683b
Moving reuseable code to utils
PujaDeshmukh17 Mar 7, 2025
36fd3cb
Adapting exsisting UTs are per code change
PujaDeshmukh17 Mar 11, 2025
30a8332
Removing rm_hold check
PujaDeshmukh17 Mar 13, 2025
c47787d
Reverting changes for createAttachmentHandler
PujaDeshmukh17 Mar 13, 2025
503e02a
Working changes with mcm
PujaDeshmukh17 Mar 17, 2025
8d968de
Exsisting UT fix
PujaDeshmukh17 Mar 17, 2025
a405e79
changes for create flow
rishikunnath2747 Mar 18, 2025
5f7835d
UT
PujaDeshmukh17 Mar 18, 2025
bd55ffc
Merge branch 'CustomPropertiesImplCreateFlow', remote-tracking branch…
rishikunnath2747 Mar 18, 2025
9e3e83f
check for filename
rishikunnath2747 Mar 18, 2025
ca4238b
create flow name update
rishikunnath2747 Mar 18, 2025
d2e87de
fix
rishikunnath2747 Mar 18, 2025
ae44226
incorrect fields check
rishikunnath2747 Mar 18, 2025
5c66baf
refactoring to change name
rishikunnath2747 Mar 19, 2025
bbf3d46
unecessary types removal
rishikunnath2747 Mar 19, 2025
3009d8d
adding caching
rishikunnath2747 Mar 20, 2025
720824f
SDMUtils test
PujaDeshmukh17 Mar 20, 2025
5fa634c
SDMUpdateAttachmentHandler UTs
PujaDeshmukh17 Mar 21, 2025
a484765
fix for boolean
rishikunnath2747 Mar 21, 2025
307f656
removing properties cache
rishikunnath2747 Mar 21, 2025
18b8c2c
removing comments
rishikunnath2747 Mar 21, 2025
550722c
fix date type
rishikunnath2747 Mar 21, 2025
236b0d4
SDMUpdateAttachmentHandler test
PujaDeshmukh17 Mar 22, 2025
171e416
Revert "SDMUpdateAttachmentHandler test"
rishikunnath2747 Mar 24, 2025
4dfd1a2
Revert "fix date type"
rishikunnath2747 Mar 24, 2025
c538cb9
annotation and check
rishikunnath2747 Mar 24, 2025
b5160e1
CreateAttachmentsHandlerUTs
rishikunnath2747 Mar 24, 2025
940b415
UpdateAttachmentHandler test
PujaDeshmukh17 Mar 25, 2025
c61b0f3
Removing print statements
PujaDeshmukh17 Mar 25, 2025
9e2c596
Utils tests to increase coverage
PujaDeshmukh17 Mar 25, 2025
128949b
Fix
rishikunnath2747 Mar 26, 2025
69691f2
adding readme content
rishikunnath2747 Mar 26, 2025
5533bc1
Uts for sdmUpdateAttachmentHandler
PujaDeshmukh17 Mar 26, 2025
7332882
readme fix
rishikunnath2747 Mar 26, 2025
7aa5890
error msg
rishikunnath2747 Mar 26, 2025
e950bc3
fix ut + readme
rishikunnath2747 Mar 26, 2025
f5a9ba5
SDMServiceImp UT
PujaDeshmukh17 Mar 26, 2025
a88dea2
Revert "SDMServiceImp UT"
rishikunnath2747 Mar 26, 2025
570fdad
UT change
rishikunnath2747 Mar 26, 2025
0e58644
SDMServiceImpl test
PujaDeshmukh17 Mar 26, 2025
73ddae3
Review comments
PujaDeshmukh17 Mar 26, 2025
85c2d49
Adding error as constant
PujaDeshmukh17 Mar 26, 2025
4fd3ea7
improving ut coverage
rishikunnath2747 Mar 26, 2025
62dbdfb
ut fix
rishikunnath2747 Mar 26, 2025
d2a24a0
ut changes
rishikunnath2747 Mar 26, 2025
37c6615
sonar fixes
rishikunnath2747 Mar 26, 2025
b8263b7
removing test
rishikunnath2747 Mar 26, 2025
bf2379c
Update cache
rishikunnath2747 Mar 27, 2025
15869cb
excluding class from sonar scan
rishikunnath2747 Mar 27, 2025
1ae1776
fix ut
rishikunnath2747 Mar 27, 2025
18be35e
fixes
rishikunnath2747 Mar 27, 2025
a5c674c
uts
rishikunnath2747 Mar 27, 2025
bf18f33
spotless fix
rishikunnath2747 Mar 27, 2025
b0e3b7b
update filename and ut coverage
rishikunnath2747 Mar 27, 2025
bda9c26
resolving conflict
rishikunnath2747 Mar 27, 2025
c36a8db
fix spotless
rishikunnath2747 Mar 27, 2025
a3fded7
Merge branch 'develop' into CustomPropFinalBranch
rishikunnath2747 Mar 27, 2025
f0c9856
sonar fixes
rishikunnath2747 Mar 27, 2025
8313746
sonar fixes
rishikunnath2747 Mar 27, 2025
1ec958e
commenting ut
rishikunnath2747 Mar 27, 2025
90c376e
sonar fix
rishikunnath2747 Mar 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/sonarqube.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
-Dsonar.junit.reportPaths=sdm/target/surefire-reports \
-Dsonar.coverage.jacoco.xmlReportPaths=sdm/target/site/jacoco/jacoco.xml \
-Dsonar.inclusions=**/*.java \
-Dsonar.exclusions=**/target/**,**/node_modules/**,sdm/src/main/test/**,cap-notebook/*.capnb,sdm/src/main/java/com/sap/cds/sdm/model/**,sdm/src/main/java/com/sap/cds/sdm/caching/CacheKey.java,sdm/src/main/java/com/sap/cds/sdm/caching/RepoKey.java,sdm/src/main/java/com/sap/cds/sdm/caching/TokenCacheKey.java \
-Dsonar.exclusions=**/target/**,**/node_modules/**,sdm/src/main/test/**,cap-notebook/*.capnb,sdm/src/main/java/com/sap/cds/sdm/model/**,sdm/src/main/java/com/sap/cds/sdm/caching/CacheKey.java,sdm/src/main/java/com/sap/cds/sdm/caching/RepoKey.java,sdm/src/main/java/com/sap/cds/sdm/caching/TokenCacheKey.java,sdm/src/main/java/com/sap/cds/sdm/caching/SecondaryTypesKey.java \
-Dsonar.java.file.suffixes=.java \
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \
-Dsonar.login=${{ secrets.SONAR_TOKEN }} \
Expand Down
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This plugin can be consumed by the CAP application deployed on BTP to store thei
- [Deploying and testing the application](#deploying-and-testing-the-application)
- [Use com.sap.cds:sdm dependency](#use-comsapcdssdm-dependency)
- [Support for Multitenancy](#support-for-multitenancy)
- [Support for Custom Properties](#support-for-custom-properties)
- [Known Restrictions](#known-restrictions)
- [Support, Feedback, Contributing](#support-feedback-contributing)
- [Code of Conduct](#code-of-conduct)
Expand Down Expand Up @@ -317,10 +318,64 @@ String response = sdmAdminService.onboardRepository(repository);
When the application is deployed as a SaaS application using the code above, tenants automatically onboard a repository upon subscription.
- When the application is deployed as a SaaS application with above code, tenants on subscribing the SaaS application gets onboarded automatically.

## Support for Custom Properties

Custom properties are supported via the usage of CMIS secondary type properties. Follow the below steps to add and use custom properties.

1. If the repository does not contain secondary types and properties, create CMIS secondary types and properties using the [Create Secondary Type API](https://api.sap.com/api/CreateSecondaryTypeApi/overview). The property definition must contain the following section for the CAP plugin to process the property.

```json
"mcm:miscellaneous": {
"isPartOfTable": "true"
}
```

With this, the secondary type and properties definition will be as per the sample given below

```json
{
"id": "Working:DocumentInfo",
"displayName": "Document Info",
"baseId": "cmis:secondary",
"parentId": "cmis:secondary",
...
},
"propertyDefinitions": {
"Working:DocumentInfoRecord": {
"id": "Working:DocumentInfoRecord",
"displayName": "Document Info Record",
...
"mcm:miscellaneous": { <-- Required section in the property definition
"isPartOfTable": "true"
}
}
}
}
```

2. Using secondary properties in CAP Application.
- Extend the `Attachments` aspect with the secondary properties in the previously created _attachment-extension.cds_ file.
- Annotate the secondary properties with `@SDM.Attachments.AdditionalProperty`.
- If the property id contains a `:`, replace it with a triple underscore `___`.

Refer the following example from a sample Bookshop app:

```cds
extend Attachments with {
Working___DocumentInfoRecord : String @SDM.Attachments.AdditionalProperty @(title: '{i18n>property1}');
}
```

> **Note**
>
> SDM supports secondary properties with data types `String`, `Boolean`, `Decimal`, `Integer` and `DateTime`.


## Known Restrictions

- Repository : This plugin does not support the use of versioned repositories.
- File size : Attachments are limited to a maximum size of 700 MB. If the repository is [onboarded](https://help.sap.com/docs/document-management-service/sap-document-management-service/internal-repository?version=Cloud&locale=en-US) with virus scan enabled for all files, attachments are limited to a maximum size of 400 MB.
- Datatypes for custom properties : Custom properties are supported for the following data types `String`, `Boolean`, `Decimal`, `Integer` and `DateTime`.

## Support, Feedback, Contributing

Expand Down
2 changes: 1 addition & 1 deletion sdm/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@
<limit implementation="org.jacoco.report.check.Limit">
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.90</minimum>
<minimum>0.80</minimum>
</limit>
<limit implementation="org.jacoco.report.check.Limit">
<counter>CLASS</counter>
Expand Down
15 changes: 15 additions & 0 deletions sdm/src/main/java/com/sap/cds/sdm/caching/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.sap.cds.sdm.caching;

import java.util.List;
import java.util.concurrent.TimeUnit;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
Expand All @@ -16,6 +17,7 @@ public class CacheConfig {
private static Cache<CacheKey, String> clientCredentialsTokenCache;
private static Cache<TokenCacheKey, String> userAuthoritiesTokenCache;
private static Cache<RepoKey, String> versionedRepoCache;
private static Cache<SecondaryTypesKey, List<String>> secondaryTypesCache;
private static final int HEAP_SIZE = 1000;
private static final int USER_TOKEN_EXPIRY = 660;
private static final int ACCESS_TOKEN_EXPIRY = 660;
Expand Down Expand Up @@ -63,6 +65,15 @@ public static void initializeCache() {
.withExpiry(
Expirations.timeToLiveExpiration(
new Duration(USER_TOKEN_EXPIRY, TimeUnit.MINUTES))));

secondaryTypesCache =
cacheManager.createCache(
"secondaryTypes",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
SecondaryTypesKey.class,
(Class<List<String>>) (Class<?>) List.class,
ResourcePoolsBuilder.heap(HEAP_SIZE))
.withExpiry(Expirations.noExpiration()));
}

public static Cache<CacheKey, String> getUserTokenCache() {
Expand All @@ -80,4 +91,8 @@ public static Cache<CacheKey, String> getClientCredentialsTokenCache() {
public static Cache<RepoKey, String> getVersionedRepoCache() {
return versionedRepoCache;
}

public static Cache<SecondaryTypesKey, List<String>> getSecondaryTypesCache() {
return secondaryTypesCache;
}
}
12 changes: 12 additions & 0 deletions sdm/src/main/java/com/sap/cds/sdm/caching/SecondaryTypesKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.sap.cds.sdm.caching;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SecondaryTypesKey {
private String repositoryId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void eventHandlers(CdsRuntimeConfigurer configurer) {

SDMService sdmService = new SDMServiceImpl(binding, connectionPool);
configurer.eventHandler(buildReadHandler());
configurer.eventHandler(new SDMCreateAttachmentsHandler(sdmService));
configurer.eventHandler(new SDMCreateAttachmentsHandler(persistenceService, sdmService));
configurer.eventHandler(new SDMUpdateAttachmentsHandler(persistenceService, sdmService));
configurer.eventHandler(new SDMAttachmentsServiceHandler(persistenceService, sdmService));
}
Expand Down
19 changes: 19 additions & 0 deletions sdm/src/main/java/com/sap/cds/sdm/constants/SDMConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ private SDMConstants() {
}

public static final String REPOSITORY_ID = System.getenv("REPOSITORY_ID");
public static final String SDM_ANNOTATION_ADDITIONALPROPERTY =
"@SDM.Attachments.AdditionalProperty";
public static final String DUPLICATE_FILE_IN_DRAFT_ERROR_MESSAGE =
"The file(s) %s have been added multiple times. Please rename and try again.";
public static final String FILES_RENAME_WARNING_MESSAGE =
Expand Down Expand Up @@ -41,6 +43,7 @@ private SDMConstants() {
"Repository with name %s and id %s onboarded successfully";
public static final String ONBOARD_REPO__ERROR_MESSAGE =
"Error in onboarding repository with name %s";
public static final String UPDATE_ATTACHMENT_ERROR = "Could not update the attachment";

public static String nameConstraintMessage(
List<String> fileNameWithRestrictedCharacters, String operation) {
Expand All @@ -62,6 +65,22 @@ public static String nameConstraintMessage(
return bulletPoints.toString();
}

public static String secondaryPropertiesError(List<String> invalidSecondaryProperties) {
// Create the base message
String prefixMessage = "The following secondary properties are not supported.\n\n";

// Initialize the StringBuilder with the formatted message prefix
StringBuilder bulletPoints = new StringBuilder(prefixMessage);

// Append each unsupported file name to the StringBuilder
for (String file : invalidSecondaryProperties) {
bulletPoints.append(String.format("\t• %s%n", file));
}
bulletPoints.append(
"\nPlease contact your administrator for assistance with any necessary adjustments.");
return bulletPoints.toString();
}

public static String getDuplicateFilesError(String filename) {
return String.format(DUPLICATE_FILES_ERROR, filename);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.sap.cds.sdm.handler.applicationservice;

import com.sap.cds.CdsData;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.sdm.constants.SDMConstants;
import com.sap.cds.sdm.handler.TokenHandler;
import com.sap.cds.sdm.model.CmisDocument;
import com.sap.cds.sdm.model.SDMCredentials;
import com.sap.cds.sdm.persistence.DBQuery;
import com.sap.cds.sdm.service.SDMService;
import com.sap.cds.sdm.utilities.SDMUtils;
import com.sap.cds.services.ServiceException;
Expand All @@ -16,18 +18,23 @@
import com.sap.cds.services.handler.annotations.Before;
import com.sap.cds.services.handler.annotations.HandlerOrder;
import com.sap.cds.services.handler.annotations.ServiceName;
import com.sap.cds.services.persistence.PersistenceService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

@ServiceName(value = "*", type = ApplicationService.class)
public class SDMCreateAttachmentsHandler implements EventHandler {

private final PersistenceService persistenceService;
private final SDMService sdmService;

public SDMCreateAttachmentsHandler(SDMService sdmService) {
public SDMCreateAttachmentsHandler(PersistenceService persistenceService, SDMService sdmService) {
this.persistenceService = persistenceService;
this.sdmService = sdmService;
}

Expand Down Expand Up @@ -82,6 +89,11 @@ private void processAttachment(
List<String> fileNameWithRestrictedCharacters,
List<String> duplicateFileNameList)
throws IOException {
String id = (String) attachment.get("ID"); // Ensure appropriate cast to String
Optional<CdsEntity> attachmentEntity =
context.getModel().findEntity(context.getTarget().getQualifiedName() + ".attachments");
String fileNameInDB;
fileNameInDB = DBQuery.getAttachmentForID(attachmentEntity.get(), persistenceService, id);
String filenameInRequest = (String) attachment.get("fileName");
String objectId = (String) attachment.get("objectId");
AuthenticationInfo authInfo = context.getAuthenticationInfo();
Expand All @@ -90,33 +102,61 @@ private void processAttachment(
SDMCredentials sdmCredentials = TokenHandler.getSDMCredentials();
String fileNameInSDM = sdmService.getObject(jwtToken, objectId, sdmCredentials);

if (fileNameInSDM != null && !fileNameInSDM.equals(filenameInRequest)) {
if (Boolean.TRUE.equals(SDMUtils.isRestrictedCharactersInName(filenameInRequest))) {
fileNameWithRestrictedCharacters.add(filenameInRequest);
attachment.replace("fileName", fileNameInSDM);
List<String> secondaryTypeProperties =
SDMUtils.getSecondaryTypeProperties(attachmentEntity, attachment);
Map<String, Object> propertiesMap = new HashMap<>();
// For each property get the value
if (!secondaryTypeProperties.isEmpty()) {
for (String property : secondaryTypeProperties) {
Object value = attachment.get(property);
propertiesMap.put(property, value);
}
}
// Get the updated secondary properties
Map<String, String> updatedSecondaryProperties =
SDMUtils.getUpdatedSecondaryProperties(
attachmentEntity, attachment, persistenceService, secondaryTypeProperties);

if (Boolean.TRUE.equals(SDMUtils.isRestrictedCharactersInName(filenameInRequest))) {
fileNameWithRestrictedCharacters.add(filenameInRequest);
attachment.replace("fileName", fileNameInSDM);
} else {
CmisDocument cmisDocument = new CmisDocument();
cmisDocument.setFileName(filenameInRequest);
cmisDocument.setObjectId(objectId);
if (fileNameInDB == null) {
if (filenameInRequest != null) {
updatedSecondaryProperties.put("filename", filenameInRequest);
} else {
throw new ServiceException("Filename cannot be empty");
}
} else {
CmisDocument cmisDocument = new CmisDocument();
cmisDocument.setFileName(filenameInRequest);
cmisDocument.setObjectId(objectId);
int responseCode = sdmService.renameAttachments(jwtToken, sdmCredentials, cmisDocument);
switch (responseCode) {
case 403:
// SDM Roles for user are missing
throw new ServiceException(SDMConstants.SDM_MISSING_ROLES_EXCEPTION_MSG, null);
if (filenameInRequest == null) {
throw new ServiceException("Filename cannot be empty");
} else if (!fileNameInDB.equals(filenameInRequest)) {
updatedSecondaryProperties.put("filename", filenameInRequest);
}
}
int responseCode =
sdmService.updateAttachments(
jwtToken, sdmCredentials, cmisDocument, updatedSecondaryProperties);
switch (responseCode) {
case 403:
// SDM Roles for user are missing
throw new ServiceException(SDMConstants.SDM_MISSING_ROLES_EXCEPTION_MSG, null);

case 409:
duplicateFileNameList.add(filenameInRequest);
attachment.replace("fileName", fileNameInSDM);
break;
case 409:
duplicateFileNameList.add(filenameInRequest);
attachment.replace("fileName", fileNameInSDM);
break;

case 200:
case 201:
// Success cases, do nothing
break;
case 200:
case 201:
// Success cases, do nothing
break;

default:
throw new ServiceException(SDMConstants.SDM_ROLES_ERROR_MESSAGE, null);
}
default:
throw new ServiceException(SDMConstants.SDM_ROLES_ERROR_MESSAGE, null);
}
}
}
Expand Down
Loading
Loading