Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2a651a0
Initial version of FS attachment feature
mofterdinger Oct 11, 2024
51091c1
move root folder configuration
mofterdinger Oct 14, 2024
1e02ddd
moved delete content to a deleted folder.
mofterdinger Oct 14, 2024
df83cc2
added readme
mofterdinger Oct 14, 2024
de41e14
readme
mofterdinger Oct 14, 2024
76ae732
enhance Attachments with size
mofterdinger Oct 14, 2024
136667d
make size readonly
mofterdinger Oct 14, 2024
99957c2
added title to size field
mofterdinger Oct 14, 2024
a7401e2
added new application service handler
mofterdinger Oct 14, 2024
f729aa8
Merge branch 'main' into fs_plugin
mofterdinger Oct 18, 2024
50a64b2
Merge branch 'main' into fs_plugin
mofterdinger Oct 21, 2024
c080fb4
Merge branch 'main' into fs_plugin
mofterdinger Oct 22, 2024
0bb45de
Merge branch 'main' into fs_plugin
mofterdinger Oct 22, 2024
f9ba32d
Merge branch 'main' into fs_plugin
mofterdinger Oct 23, 2024
9b01d58
removed size from model
mofterdinger Oct 23, 2024
3904084
added handler for new draft event
mofterdinger Oct 24, 2024
bddc22c
added tenant to storage path
mofterdinger Oct 24, 2024
7320aa5
Merge branch 'main' into fs_plugin
mofterdinger Oct 30, 2024
f4bf3f0
Merge branch 'main' into fs_plugin
mofterdinger Oct 30, 2024
4f3fed3
fixed compile issue
mofterdinger Oct 30, 2024
ae6f16a
Merge branch 'main' into fs_plugin
mofterdinger Oct 31, 2024
e8e474e
Merge branch 'main' into fs_plugin
mofterdinger Nov 4, 2024
0cbb0d7
Merge branch 'main' into fs_plugin
mofterdinger Nov 13, 2024
b00910a
Merge branch 'main' into fs_plugin
mofterdinger Dec 9, 2024
e72900c
moved to examples folder
mofterdinger Dec 9, 2024
d90ef1e
moved README
mofterdinger Dec 9, 2024
031b1d2
exclude examples folder from sonarqube scan
mofterdinger Dec 9, 2024
b942ea3
pom cleanup
mofterdinger Dec 9, 2024
5e08f88
removed handler for application service events
mofterdinger Dec 9, 2024
343fa39
added test cases
mofterdinger Dec 9, 2024
0aaa3d8
Merge branch 'main' into fs_plugin
mofterdinger Dec 10, 2024
1fd3dba
update tests
mofterdinger Dec 10, 2024
cd72638
Merge branch 'fs_plugin' of github.com:cap-java/cds-feature-attachmen…
mofterdinger Dec 10, 2024
3a42228
Merge branch 'main' into fs_plugin
mofterdinger Dec 17, 2024
95cc294
Merge branch 'main' into fs_plugin
mofterdinger Jan 17, 2025
646fcc2
Merge branch 'main' into fs_plugin
mofterdinger Jan 29, 2025
c867609
Merge branch 'main' into fs_plugin
mofterdinger Mar 24, 2025
ad94437
determine foreign-key elements in attachment entity.
mofterdinger Mar 24, 2025
cb6550c
added new event handler
mofterdinger Mar 27, 2025
5d5e5a6
improved stability
mofterdinger Mar 27, 2025
85c14ad
respect all foreign keys
mofterdinger Mar 27, 2025
4a76a4c
remove unused code
mofterdinger Mar 27, 2025
c2bae49
renamed class
mofterdinger Mar 27, 2025
9c24883
Bump logback-classic to 1.5.18
mofterdinger Mar 27, 2025
1d4c252
Bump awaitility to 4.3.0
mofterdinger Mar 27, 2025
e86468c
workaround for wrong mime type provided by SAP UI5.
mofterdinger Mar 27, 2025
ba3381a
Merge branch 'main' into fs_plugin
mofterdinger May 7, 2025
623aea6
Merge branch 'main' into fs_plugin
mofterdinger Jul 10, 2025
3b2de34
Merge branch 'main' into fs_plugin
mofterdinger Jul 11, 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: 0 additions & 2 deletions cds-feature-attachments/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,11 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
7 changes: 7 additions & 0 deletions examples/cds-feature-attachments-fs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### General Info

This artifact demonstrates how to implement a custom storage for the cds-feature-attachments. This is just a sample implementation using a filessystem as content storage. It's not supposed for productive usage.

### Implementation details

This artifact provides a custom handlers for events from the [AttachmentService](../cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/service/AttachmentService.java).
74 changes: 74 additions & 0 deletions examples/cds-feature-attachments-fs/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.sap.cds</groupId>
<artifactId>cds-feature-attachments-root</artifactId>
<version>${revision}</version>
<relativePath>../..</relativePath>
</parent>

<artifactId>cds-feature-attachments-fs</artifactId>
<packaging>jar</packaging>

<name>CDS Feature for Attachments - Filesystem</name>
<url>https://cap.cloud.sap/docs/plugins/#attachments</url>

<dependencies>
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-feature-attachments</artifactId>
</dependency>

<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-services-api</artifactId>
</dependency>

<!-- TESTS -->
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-services-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>

<plugin>
<groupId>com.sap.cds</groupId>
<artifactId>cds-maven-plugin</artifactId>
<executions>
<execution>
<id>cds.resolve</id>
<goals>
<goal>resolve</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>

</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.sap.cds.feature.attachments.fs.configuration;

import java.io.IOException;
import java.nio.file.Path;

import org.apache.commons.io.FileUtils;

import com.sap.cds.feature.attachments.fs.handler.FSApplicationServiceHandler;
import com.sap.cds.feature.attachments.fs.handler.FSAttachmentsServiceHandler;
import com.sap.cds.services.runtime.CdsRuntimeConfiguration;
import com.sap.cds.services.runtime.CdsRuntimeConfigurer;

/**
* The class registers the event handlers for the attachments feature based on filesystem.
*/
public class Registration implements CdsRuntimeConfiguration {

@Override
public void eventHandlers(CdsRuntimeConfigurer configurer) {
configurer.eventHandler(new FSApplicationServiceHandler());

Path tmpPath = FileUtils.getTempDirectory().toPath();

Check warning

Code scanning / CodeQL

Local information disclosure in a temporary directory

Local information disclosure vulnerability from [system temp directory](1) due to use of file or directory readable by other local users.
Path rootFolder = tmpPath.resolve("com.sap.cds.cds-feature-attachments-fs");

try {
configurer.eventHandler(new FSAttachmentsServiceHandler(rootFolder));
} catch (IOException e) {
throw new IllegalStateException("Error while creating the FSAttachmentsServiceHandler", e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.sap.cds.feature.attachments.fs.handler;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cds.CdsData;
import com.sap.cds.services.cds.ApplicationService;
import com.sap.cds.services.cds.CdsCreateEventContext;
import com.sap.cds.services.cds.CdsUpdateEventContext;
import com.sap.cds.services.draft.DraftCreateEventContext;
import com.sap.cds.services.handler.EventHandler;
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.utils.OrderConstants;

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

private static final Logger logger = LoggerFactory.getLogger(FSApplicationServiceHandler.class);

@Before
public void processBeforeNewDraft(DraftCreateEventContext context, List<CdsData> data) {
logger.info("Creating new draft for entity {}", context.getTarget());
}

@Before
@HandlerOrder(OrderConstants.Before.CHECK_CAPABILITIES + 1)
public void processCreateAttachments(CdsCreateEventContext context, List<CdsData> data) {
// TODO: add real implementation
}

@Before
@HandlerOrder(OrderConstants.Before.CHECK_CAPABILITIES + 1)
public void processUpdateAttachments(CdsUpdateEventContext context, List<CdsData> data) {
// TODO: add real implementation
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**************************************************************************
* (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
**************************************************************************/
package com.sap.cds.feature.attachments.fs.handler;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cds.feature.attachments.generated.cds4j.sap.attachments.Attachments;
import com.sap.cds.feature.attachments.generated.cds4j.sap.attachments.MediaData;
import com.sap.cds.feature.attachments.generated.cds4j.sap.attachments.StatusCode;
import com.sap.cds.feature.attachments.service.AttachmentService;
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentCreateEventContext;
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentMarkAsDeletedEventContext;
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentReadEventContext;
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentRestoreEventContext;
import com.sap.cds.services.EventContext;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.On;
import com.sap.cds.services.handler.annotations.ServiceName;

/**
* This class is an event handler that is called when an attachment is created, marked as deleted, restored or read.
*/
@ServiceName(value = "*", type = AttachmentService.class)
public class FSAttachmentsServiceHandler implements EventHandler {

private static final Logger logger = LoggerFactory.getLogger(FSAttachmentsServiceHandler.class);

private final Path rootFolder;

public FSAttachmentsServiceHandler(Path rootFolder) throws IOException {
this.rootFolder = rootFolder;
if (!Files.exists(this.rootFolder)) {
Files.createDirectories(this.rootFolder);
}
}

@On
public void createAttachment(AttachmentCreateEventContext context) throws IOException {
logger.info("FS Attachment Service handler called for creating attachment for entity name: {}",
context.getAttachmentEntity().getQualifiedName());
String contentId = (String) context.getAttachmentIds().get(Attachments.ID);

MediaData data = context.getData();
data.setStatus(StatusCode.CLEAN);

try (InputStream input = data.getContent()) {
Path contentPath = getContentPath(context, contentId);
Files.createDirectories(contentPath.getParent());
Files.copy(input, contentPath);

context.setIsInternalStored(false);
context.setContentId(contentId);
context.setCompleted();
}
}

@On
public void markAttachmentAsDeleted(AttachmentMarkAsDeletedEventContext context) throws IOException {
logger.info("Marking attachment as deleted with document id: {}", context.getContentId());

Path contenPath = getContentPath(context, context.getContentId());
Path parent = contenPath.getParent();
Path destPath = getDeletedFolder(context).resolve(parent.getFileName());

FileUtils.moveDirectory(parent.toFile(), destPath.toFile());
context.setCompleted();
}

@On
public void restoreAttachment(AttachmentRestoreEventContext context) {
logger.info("FS Attachment Service handler called for restoring attachment for timestamp: {}",
context.getRestoreTimestamp());

// nothing to do as data are stored in the database and handled by the database
context.setCompleted();
}

@On
public void readAttachment(AttachmentReadEventContext context) throws IOException {
logger.info("FS Attachment Service handler called for reading attachment with document id: {}",
context.getContentId());
InputStream fileInputStream = Files.newInputStream(getContentPath(context, context.getContentId()));
context.getData().setContent(fileInputStream);
context.setCompleted();
}

private Path getContentPath(EventContext context, String contentId) {
return this.rootFolder.resolve("%s/%s/content.bin".formatted(getTenant(context), contentId));
}

private Path getDeletedFolder(EventContext context) {
return this.rootFolder.resolve("%s/deleted".formatted(getTenant(context)));
}

private static String getTenant(EventContext context) {
String tenant = context.getUserInfo().getTenant();
if (tenant == null) {
tenant = "default";
}
return tenant;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.sap.cds.feature.attachments.fs.configuration.Registration
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

<modules>
<module>cds-feature-attachments</module>
<module>examples/cds-feature-attachments-fs</module>
<module>integration-tests</module>
</modules>

Expand Down Expand Up @@ -75,6 +76,20 @@
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.9</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-feature-attachments</artifactId>
Expand Down