Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@
import com.sap.cds.feature.attachments.handler.applicationservice.UpdateAttachmentsHandler;
import com.sap.cds.feature.attachments.handler.applicationservice.helper.ThreadLocalDataStorage;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.CreateAttachmentEvent;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.DefaultModifyAttachmentEventFactory;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.ModifyAttachmentEventFactory;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.DoNothingAttachmentEvent;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.MarkAsDeletedAttachmentEvent;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.ModifyAttachmentEvent;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.UpdateAttachmentEvent;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.readhelper.AttachmentStatusValidator;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.transaction.CreationChangeSetListener;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.transaction.ListenerProvider;
import com.sap.cds.feature.attachments.handler.common.DefaultAssociationCascader;
import com.sap.cds.feature.attachments.handler.common.DefaultAttachmentsReader;
import com.sap.cds.feature.attachments.handler.common.AssociationCascader;
import com.sap.cds.feature.attachments.handler.common.AttachmentsReader;
import com.sap.cds.feature.attachments.handler.draftservice.DraftActiveAttachmentsHandler;
import com.sap.cds.feature.attachments.handler.draftservice.DraftCancelAttachmentsHandler;
import com.sap.cds.feature.attachments.handler.draftservice.DraftPatchAttachmentsHandler;
Expand Down Expand Up @@ -102,7 +101,7 @@ public void eventHandlers(CdsRuntimeConfigurer configurer) {
var deleteContentEvent = new MarkAsDeletedAttachmentEvent(outboxedAttachmentService);
var eventFactory = buildAttachmentEventFactory(attachmentService, deleteContentEvent,
outboxedAttachmentService);
var attachmentsReader = new DefaultAttachmentsReader(new DefaultAssociationCascader(), persistenceService);
var attachmentsReader = new AttachmentsReader(new AssociationCascader(), persistenceService);
ThreadLocalDataStorage storage = new ThreadLocalDataStorage();

// register event handlers for application service, if at least one application service is available
Expand Down Expand Up @@ -157,15 +156,15 @@ private static MalwareScanClient buildMalwareScanClient(CdsEnvironment environme
return null;
}

private static DefaultModifyAttachmentEventFactory buildAttachmentEventFactory(AttachmentService attachmentService,
ModifyAttachmentEvent deleteContentEvent, AttachmentService outboxedAttachmentService) {
private static ModifyAttachmentEventFactory buildAttachmentEventFactory(AttachmentService attachmentService,
MarkAsDeletedAttachmentEvent deleteContentEvent, AttachmentService outboxedAttachmentService) {
ListenerProvider creationChangeSetListener = (contentId, cdsRuntime) -> new CreationChangeSetListener(contentId,
cdsRuntime, outboxedAttachmentService);
var createAttachmentEvent = new CreateAttachmentEvent(attachmentService, creationChangeSetListener);
var updateAttachmentEvent = new UpdateAttachmentEvent(createAttachmentEvent, deleteContentEvent);

var doNothingAttachmentEvent = new DoNothingAttachmentEvent();
return new DefaultModifyAttachmentEventFactory(createAttachmentEvent, updateAttachmentEvent, deleteContentEvent,
return new ModifyAttachmentEventFactory(createAttachmentEvent, updateAttachmentEvent, deleteContentEvent,
doNothingAttachmentEvent);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
**************************************************************************/
package com.sap.cds.feature.attachments.handler.applicationservice;

import static java.util.Objects.requireNonNull;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
Expand All @@ -28,9 +30,8 @@
import com.sap.cds.services.utils.OrderConstants;

/**
* The class {@link CreateAttachmentsHandler} is an event handler that is
* responsible for creating attachments for entities.
* It is called before a create event is executed.
* The class {@link CreateAttachmentsHandler} is an event handler that is responsible for creating attachments for
* entities. It is called before a create event is executed.
*/
@ServiceName(value = "*", type = ApplicationService.class)
public class CreateAttachmentsHandler implements EventHandler {
Expand All @@ -42,8 +43,8 @@ public class CreateAttachmentsHandler implements EventHandler {
private final CdsDataProcessor processor = CdsDataProcessor.create();

public CreateAttachmentsHandler(ModifyAttachmentEventFactory eventFactory, ThreadDataStorageReader storageReader) {
this.eventFactory = eventFactory;
this.storageReader = storageReader;
this.eventFactory = requireNonNull(eventFactory, "eventFactory must not be null");
this.storageReader = requireNonNull(storageReader, "storageReader must not be null");
}

@Before
Expand All @@ -61,12 +62,13 @@ public void processBefore(CdsCreateEventContext context, List<CdsData> data) {

logger.debug("Processing before create event for entity {}", context.getTarget().getName());
setKeysInData(context.getTarget(), data);
ModifyApplicationHandlerHelper.handleAttachmentForEntities(context.getTarget(), data, new ArrayList<>(), eventFactory,
context);
ModifyApplicationHandlerHelper.handleAttachmentForEntities(context.getTarget(), data, new ArrayList<>(),
eventFactory, context);
}

private void setKeysInData(CdsEntity entity, List<CdsData> data) {
processor.addGenerator((path, element, type) -> element.isKey() && element.getType().isSimpleType(CdsBaseType.UUID),
processor.addGenerator(
(path, element, type) -> element.isKey() && element.getType().isSimpleType(CdsBaseType.UUID),
(path, element, isNull) -> UUID.randomUUID().toString()).process(data, entity);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
**************************************************************************/
package com.sap.cds.feature.attachments.handler.applicationservice;

import static java.util.Objects.requireNonNull;

import java.io.InputStream;
import java.util.List;

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

import com.sap.cds.CdsData;
import com.sap.cds.CdsDataProcessor;
import com.sap.cds.CdsDataProcessor.Converter;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.ModifyAttachmentEvent;
import com.sap.cds.feature.attachments.handler.applicationservice.processor.modifyevents.MarkAsDeletedAttachmentEvent;
import com.sap.cds.feature.attachments.handler.common.ApplicationHandlerHelper;
import com.sap.cds.feature.attachments.handler.common.AttachmentsReader;
import com.sap.cds.services.cds.ApplicationService;
Expand All @@ -31,25 +34,26 @@ public class DeleteAttachmentsHandler implements EventHandler {
private static final Logger logger = LoggerFactory.getLogger(DeleteAttachmentsHandler.class);

private final AttachmentsReader attachmentsReader;
private final ModifyAttachmentEvent deleteContentAttachmentEvent;
private final MarkAsDeletedAttachmentEvent deleteEvent;

public DeleteAttachmentsHandler(AttachmentsReader attachmentsReader,
ModifyAttachmentEvent deleteContentAttachmentEvent) {
this.attachmentsReader = attachmentsReader;
this.deleteContentAttachmentEvent = deleteContentAttachmentEvent;
public DeleteAttachmentsHandler(AttachmentsReader attachmentsReader, MarkAsDeletedAttachmentEvent deleteEvent) {
this.attachmentsReader = requireNonNull(attachmentsReader, "attachmentsReader must not be null");
this.deleteEvent = requireNonNull(deleteEvent, "deleteEvent must not be null");
}

@Before
@HandlerOrder(HandlerOrder.LATE)
public void processBefore(CdsDeleteEventContext context) {
logger.debug("Processing before delete event for entity {}", context.getTarget().getName());

var attachments = attachmentsReader.readAttachments(context.getModel(), context.getTarget(), context.getCqn());
List<CdsData> attachments = attachmentsReader.readAttachments(context.getModel(), context.getTarget(),
context.getCqn());

Converter converter = (path, element, value) -> deleteContentAttachmentEvent.processEvent(path,
(InputStream) value, CdsData.create(path.target().values()), context);
Converter converter = (path, element, value) -> deleteEvent.processEvent(path, (InputStream) value,
CdsData.create(path.target().values()), context);

CdsDataProcessor.create().addConverter(ApplicationHandlerHelper.MEDIA_CONTENT_FILTER, converter).process(attachments, context.getTarget());
CdsDataProcessor.create().addConverter(ApplicationHandlerHelper.MEDIA_CONTENT_FILTER, converter)
.process(attachments, context.getTarget());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.sap.cds.feature.attachments.service.AttachmentService;
import com.sap.cds.feature.attachments.service.malware.AsyncMalwareScanExecutor;
import com.sap.cds.ql.CQL;
import com.sap.cds.ql.cqn.CqnSelect;
import com.sap.cds.ql.cqn.Path;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsElementDefinition;
Expand All @@ -41,13 +42,11 @@
import com.sap.cds.services.handler.annotations.ServiceName;

/**
* The class {@link ReadAttachmentsHandler} is an event handler that is
* responsible for reading attachments for entities.
* In the before read event, it modifies the CQN to include the content ID and status.
* In the after read event, it adds a proxy for the stream of the attachments service to the data.
* Only if the data are read the proxy forwards the request to the attachment service to read the attachment.
* This is needed to have a filled stream in the data to enable the OData V4 adapter to enrich the data that
* a link to the content can be shown on the UI.
* The class {@link ReadAttachmentsHandler} is an event handler that is responsible for reading attachments for
* entities. In the before read event, it modifies the CQN to include the content ID and status. In the after read
* event, it adds a proxy for the stream of the attachments service to the data. Only if the data are read the proxy
* forwards the request to the attachment service to read the attachment. This is needed to have a filled stream in the
* data to enable the OData V4 adapter to enrich the data that a link to the content can be shown on the UI.
*/
@ServiceName(value = "*", type = ApplicationService.class)
public class ReadAttachmentsHandler implements EventHandler {
Expand All @@ -58,7 +57,7 @@ public class ReadAttachmentsHandler implements EventHandler {
private final AttachmentStatusValidator attachmentStatusValidator;
private final AsyncMalwareScanExecutor asyncMalwareScanExecutor;

public ReadAttachmentsHandler(AttachmentService attachmentService,
public ReadAttachmentsHandler(AttachmentService attachmentService,
AttachmentStatusValidator attachmentStatusValidator, AsyncMalwareScanExecutor asyncMalwareScanExecutor) {
this.attachmentService = attachmentService;
this.attachmentStatusValidator = attachmentStatusValidator;
Expand All @@ -70,10 +69,10 @@ public ReadAttachmentsHandler(AttachmentService attachmentService,
public void processBefore(CdsReadEventContext context) {
logger.debug("Processing before read event for entity {}", context.getTarget().getName());

var cdsModel = context.getModel();
var fieldNames = getAttachmentAssociations(cdsModel, context.getTarget(), "", new ArrayList<>());
CdsModel cdsModel = context.getModel();
List<String> fieldNames = getAttachmentAssociations(cdsModel, context.getTarget(), "", new ArrayList<>());
if (!fieldNames.isEmpty()) {
var resultCqn = CQL.copy(context.getCqn(), new BeforeReadItemsModifier(fieldNames));
CqnSelect resultCqn = CQL.copy(context.getCqn(), new BeforeReadItemsModifier(fieldNames));
context.setCqn(resultCqn);
}
}
Expand All @@ -88,41 +87,42 @@ public void processAfter(CdsReadEventContext context, List<CdsData> data) {

Converter converter = (path, element, value) -> {
logger.info("Processing after read event for entity {}", element.getName());
var contentId = (String) path.target().values().get(Attachments.CONTENT_ID);
var status = (String) path.target().values().get(Attachments.STATUS);
var content = (InputStream) path.target().values().get(Attachments.CONTENT);
var contentExists = Objects.nonNull(content);
String contentId = (String) path.target().values().get(Attachments.CONTENT_ID);
String status = (String) path.target().values().get(Attachments.STATUS);
InputStream content = (InputStream) path.target().values().get(Attachments.CONTENT);
boolean contentExists = Objects.nonNull(content);
if (Objects.nonNull(contentId) || contentExists) {
verifyStatus(path, status, contentId, contentExists);
Supplier<InputStream> supplier = Objects.nonNull(content) ? () -> content : () -> attachmentService.readAttachment(
contentId);
Supplier<InputStream> supplier = Objects.nonNull(content) ? () -> content
: () -> attachmentService.readAttachment(contentId);
return new LazyProxyInputStream(supplier, attachmentStatusValidator, status);
} else {
return value;
}
};

CdsDataProcessor.create().addConverter(ApplicationHandlerHelper.MEDIA_CONTENT_FILTER, converter).process(data, context.getTarget());
CdsDataProcessor.create().addConverter(ApplicationHandlerHelper.MEDIA_CONTENT_FILTER, converter).process(data,
context.getTarget());
}

private List<String> getAttachmentAssociations(CdsModel model, CdsEntity entity, String associationName,
List<String> processedEntities) {
var associationNames = new ArrayList<String>();
List<String> associationNames = new ArrayList<String>();
if (ApplicationHandlerHelper.isMediaEntity(entity)) {
associationNames.add(associationName);
}

Map<String, CdsEntity> annotatedEntitiesMap = entity.associations()
.collect(Collectors.toMap(CdsElementDefinition::getName,
element -> element.getType().as(CdsAssociationType.class).getTarget()));
Map<String, CdsEntity> annotatedEntitiesMap = entity.associations().collect(Collectors.toMap(
CdsElementDefinition::getName, element -> element.getType().as(CdsAssociationType.class).getTarget()));

if (annotatedEntitiesMap.isEmpty()) {
return associationNames;
}

for (var associatedElement : annotatedEntitiesMap.entrySet()) {
if (!associationNames.contains(associatedElement.getKey()) && !processedEntities.contains(
associatedElement.getKey()) && !Drafts.SIBLING_ENTITY.equals(associatedElement.getKey())) {
if (!associationNames.contains(associatedElement.getKey())
&& !processedEntities.contains(associatedElement.getKey())
&& !Drafts.SIBLING_ENTITY.equals(associatedElement.getKey())) {
processedEntities.add(associatedElement.getKey());
var result = getAttachmentAssociations(model, associatedElement.getValue(), associatedElement.getKey(),
processedEntities);
Expand Down
Loading