Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIN-303 send content digest for alfresco insight connector #848

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
@@ -0,0 +1,74 @@
/*
* #%L
* Alfresco HX Insight Connector
* %%
* Copyright (C) 2023 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.hxi_connector.live_ingester.domain.usecase.e2e.repository;

import static org.alfresco.hxi_connector.live_ingester.util.ContainerSupport.REQUEST_ID_PLACEHOLDER;

import org.junit.jupiter.api.Test;

import org.alfresco.hxi_connector.live_ingester.util.E2ETestBase;
import org.alfresco.hxi_connector.live_ingester.util.insight_api.HxInsightRequest;

public class CheckDigestRequestIntegrationTest extends E2ETestBase
{
@Test
void testCheckDigestRequest()
{
// given
containerSupport.prepareHxInsightToReturnSuccess();

// when
String sourceId = "alfresco-dummy-source-id-0a63de491876";
String objectId = "368818d9-dddd-4b8b-8eab-e050253d7f61";
String digest = "fake-digest-identifier";
String url = String.format("/v1/check-digest/%s/%s/%s", sourceId, objectId, digest);

HxInsightRequest request = new HxInsightRequest(url, null, null);

String repoEvent = """
{
"sourceId": "%s",
"objectId": "%s",
"digest": "%s"
}
""".formatted(sourceId, objectId, digest);
containerSupport.raiseRepoEvent(repoEvent);

// then
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these comments necessary ? They just take up unnecessary space :)

containerSupport.expectHxIngestMessageReceived(request.body());

String expectedATSRequest = """
{
"requestId": "%s",
"nodeRef": "workspace://SpacesStore/d71dd823-82c7-477c-8490-04cb0e826e65",
"targetMediaType": "application/pdf",
"clientData": "{\\"nodeRef\\":\\"d71dd823-82c7-477c-8490-04cb0e826e65\\",\\"targetMimeType\\":\\"application/pdf\\",\\"retryAttempt\\":0,\\"timestamp\\":1611227656423}",
"transformOptions": { "timeout":"20000" },
"replyQueue": "org.alfresco.hxinsight-connector.transform.response"
}""".formatted(REQUEST_ID_PLACEHOLDER);
containerSupport.verifyATSRequestReceived(expectedATSRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,18 @@ void testRequestToPresignedUrls()
{
HxInsightRequest hxInsightRequest = RequestLoader.load("/rest/hxinsight/requests/get-presigned-urls.yml");

Request request = makeRequest(hxInsightRequest);
Request request = makeRequest(hxInsightRequest, "POST");

assertThat(openApiInteractionValidator.validateRequest(request).getMessages()).isEmpty();
}

@SneakyThrows
@Test
void testCheckDigestEvent()
{
HxInsightRequest hxInsightRequest = RequestLoader.load("/rest/hxinsight/requests/check-digest.yml");

Request request = makeRequest(hxInsightRequest, "GET");

assertThat(openApiInteractionValidator.validateRequest(request).getMessages()).isEmpty();
}
Expand All @@ -70,7 +81,7 @@ void testUploadReferencesRequestToIngestionEvents()
{
HxInsightRequest hxInsightRequest = RequestLoader.load("/rest/hxinsight/requests/upload-references-document.yml");

Request request = makeRequest(hxInsightRequest);
Request request = makeRequest(hxInsightRequest, "POST");

assertThat(openApiInteractionValidator.validateRequest(request).getMessages()).isEmpty();
}
Expand All @@ -81,7 +92,7 @@ void testCreateOrUpdateRequestToIngestionEvents()
{
HxInsightRequest hxInsightRequest = RequestLoader.load("/rest/hxinsight/requests/create-or-update-document.yml");

Request request = makeRequest(hxInsightRequest);
Request request = makeRequest(hxInsightRequest, "POST");

assertThat(openApiInteractionValidator.validateRequest(request).getMessages()).isEmpty();
}
Expand All @@ -91,14 +102,22 @@ void testDeleteRequestToIngestionEvents()
{
HxInsightRequest hxInsightRequest = RequestLoader.load("/rest/hxinsight/requests/delete-document.yml");

Request request = makeRequest(hxInsightRequest);
Request request = makeRequest(hxInsightRequest, "POST");

assertThat(openApiInteractionValidator.validateRequest(request).getMessages()).isEmpty();
}

private static Request makeRequest(HxInsightRequest hxInsightRequest)
private static Request makeRequest(HxInsightRequest hxInsightRequest, String method)
{
SimpleRequest.Builder builder = SimpleRequest.Builder.post(hxInsightRequest.url());
SimpleRequest.Builder builder;
if ("GET".equalsIgnoreCase(method))
{
builder = SimpleRequest.Builder.get(hxInsightRequest.url());
}
else
{
builder = SimpleRequest.Builder.post(hxInsightRequest.url());
}
hxInsightRequest.headers().forEach(builder::withHeader);
return builder.withBody(hxInsightRequest.body()).build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
url: /v1/check-digest/{{sourceId}}/{{objectId}}/{{digest}}
headers:
authorization: string
content-type: application/json
hxp-environment: string
user-agent: string
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* #%L
* Alfresco HX Insight Connector
* %%
* Copyright (C) 2023 - 2024 Alfresco Software Limited
* Copyright (C) 2023 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
Expand Down Expand Up @@ -41,4 +41,5 @@ public class ContentMetadata
private final String sourceMimeType;
private final Long size;
private final String name;
private final String digestIdentifier;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* #%L
* Alfresco HX Insight Connector
* %%
* Copyright (C) 2023 - 2024 Alfresco Software Limited
* Copyright (C) 2023 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
Expand Down Expand Up @@ -49,7 +49,7 @@ public FileMetadata(ContentProperty contentProperty)
contentType = contentProperty.mimeType();
if (contentProperty.sourceMimeType() != null || contentProperty.sourceSizeInBytes() != null || contentProperty.sourceFileName() != null)
{
contentMetadata = new ContentMetadata(contentProperty.sourceMimeType(), contentProperty.sourceSizeInBytes(), contentProperty.sourceFileName());
contentMetadata = new ContentMetadata(contentProperty.sourceMimeType(), contentProperty.sourceSizeInBytes(), contentProperty.sourceFileName(), contentProperty.digestIdentifier());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HashSet;
import java.util.Set;

import lombok.NoArgsConstructor;

@NoArgsConstructor(access = PRIVATE)
@SuppressWarnings({"PMD.PrematureDeclaration", "PMD.SimplifyBooleanReturns"})
public final class ContentUtils
{
private static final Set<String> SEEN_DIGESTS = new HashSet<>();

public static String generateDigestIdentifier(DigestIdentifierParams params)
{
String input = params.nodeId() + "-" + params.propertyName() + "-" + params.versionNumber();
Expand All @@ -53,4 +57,14 @@ public static String generateDigestIdentifier(DigestIdentifierParams params)
throw new IllegalArgumentException("Invalid digest identifier algorithm: " + params.digestAlgorithm(), e);
}
}

public static boolean isContentSeenBefore(String digestIdentifier)
{
return SEEN_DIGESTS.contains(digestIdentifier);
}

public static void markContentAsSeen(String digestIdentifier)
{
SEEN_DIGESTS.add(digestIdentifier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* #%L
* Alfresco HX Insight Connector
* %%
* Copyright (C) 2023 - 2024 Alfresco Software Limited
* Copyright (C) 2023 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
Expand All @@ -27,7 +27,7 @@

import static org.alfresco.hxi_connector.common.util.EnsureUtils.ensureNotBlank;

public record ContentProperty(String propertyName, String id, String mimeType, String sourceMimeType, Long sourceSizeInBytes, String sourceFileName)
public record ContentProperty(String propertyName, String id, String mimeType, String sourceMimeType, Long sourceSizeInBytes, String sourceFileName, String digestIdentifier)
{
public ContentProperty
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import org.alfresco.hxi_connector.live_ingester.adapters.messaging.repository.util.ContentUtils;
import org.alfresco.hxi_connector.live_ingester.adapters.messaging.repository.util.DigestIdentifierParams;
import org.alfresco.hxi_connector.live_ingester.domain.ports.ingestion_engine.storage.IngestionEngineStorageClient;
import org.alfresco.hxi_connector.live_ingester.domain.ports.ingestion_engine.storage.model.IngestContentResponse;
import org.alfresco.hxi_connector.live_ingester.domain.ports.transform_engine.TransformEngineFileStorage;
Expand All @@ -58,10 +61,33 @@ public class IngestContentCommandHandler
private final TransformEngineFileStorage transformEngineFileStorage;
private final IngestionEngineStorageClient ingestionEngineStorageClient;

@Value("${hyland-experience.storage.digest-algorithm:SHA-256}")
private String digestAlgorithm;

public void handle(TriggerContentIngestionCommand command)
{
TransformRequest transformRequest = new TransformRequest(command.nodeId(), command.mimeType(), command.timestamp());
transformRequester.requestTransform(transformRequest);
IngestNodeCommand ingestNodeCommand = new IngestNodeCommand(command.nodeId(), CREATE_OR_UPDATE, Set.of(), command.timestamp());
ingestNodeCommandHandler.handle(ingestNodeCommand);

String digestIdentifier;
try
{
digestIdentifier = ContentUtils.generateDigestIdentifier(new DigestIdentifierParams(digestAlgorithm, command.nodeId(), CONTENT_PROPERTY, "1.0"));
}
catch (IllegalArgumentException e)
{
log.atError().log("Error generating digest identifier for node: {}", command.nodeId());
return;
}

boolean isContentSeenBefore = ContentUtils.isContentSeenBefore(digestIdentifier);

if (!isContentSeenBefore)
{
ContentUtils.markContentAsSeen(digestIdentifier);
TransformRequest transformRequest = new TransformRequest(command.nodeId(), command.mimeType(), command.timestamp());
transformRequester.requestTransform(transformRequest);
}
}

public void handle(IngestContentCommand command)
Expand All @@ -75,7 +101,6 @@ public void handle(IngestContentCommand command)
log.atDebug().log("Transform :: Rendition download complete for node: {} as file with ID: {}", nodeId, fileId);

IngestContentResponse ingestContentResponse = ingestionEngineStorageClient.upload(downloadedFile, command.mimeType(), nodeId);

Set<PropertyDelta<?>> properties = Set.of(
contentPropertyUpdated(CONTENT_PROPERTY, ingestContentResponse.transferId(), ingestContentResponse.mimeType()));
IngestNodeCommand ingestNodeCommand = new IngestNodeCommand(nodeId, CREATE_OR_UPDATE, properties, command.timestamp());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* #%L
* Alfresco HX Insight Connector
* %%
* Copyright (C) 2023 - 2024 Alfresco Software Limited
* Copyright (C) 2023 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
Expand Down Expand Up @@ -45,21 +45,23 @@ public class ContentPropertyUpdated extends PropertyDelta<String>
private String sourceMimeType;
private Long sourceSizeInBytes;
private String sourceFileName;
private String digestIdentifier;

public ContentPropertyUpdated(String propertyName, String id, String mimeType, String sourceMimeType, Long sourceSizeInBytes, String sourceFileName)
public ContentPropertyUpdated(String propertyName, String id, String mimeType, String sourceMimeType, Long sourceSizeInBytes, String sourceFileName, String digestIdentifier)
{
super(propertyName);
this.id = id;
this.mimeType = mimeType;
this.sourceMimeType = sourceMimeType;
this.sourceSizeInBytes = sourceSizeInBytes;
this.sourceFileName = sourceFileName;
this.digestIdentifier = digestIdentifier;
}

@Override
public void applyOn(UpdateNodeEvent event)
{
ContentProperty contentProperty = new ContentProperty(getPropertyName(), id, mimeType, sourceMimeType, sourceSizeInBytes, sourceFileName);
ContentProperty contentProperty = new ContentProperty(getPropertyName(), id, mimeType, sourceMimeType, sourceSizeInBytes, sourceFileName, digestIdentifier);
event.addContentInstruction(contentProperty);
}

Expand All @@ -83,6 +85,7 @@ public static class ContentPropertyUpdatedBuilder
private String sourceMimeType;
private Long sourceSizeInBytes;
private String sourceFileName;
private String digestIdentifier;

public ContentPropertyUpdatedBuilder id(String id)
{
Expand Down Expand Up @@ -114,9 +117,15 @@ public ContentPropertyUpdatedBuilder sourceFileName(String sourceFileName)
return this;
}

public ContentPropertyUpdatedBuilder digestIdentifier(String digestIdentifier)
{
this.digestIdentifier = digestIdentifier;
return this;
}

public ContentPropertyUpdated build()
{
return new ContentPropertyUpdated(propertyName, id, mimeType, sourceMimeType, sourceSizeInBytes, sourceFileName);
return new ContentPropertyUpdated(propertyName, id, mimeType, sourceMimeType, sourceSizeInBytes, sourceFileName, digestIdentifier);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public void shouldSetContentProperty()
{
UpdateNodeEvent event = new UpdateNodeEvent(NODE_ID, CREATE_OR_UPDATE, SOURCE_ID, TIMESTAMP)
.addContentInstruction(new ContentProperty(CONTENT_PROPERTY, "content-id", "application/pdf",
"application/msword", 123L, "something.doc"));
"application/msword", 123L, "something.doc", "fake-digest-identifier"));

String expectedJson = """
[
Expand All @@ -143,6 +143,7 @@ public void shouldSetContentProperty()
"content-metadata": {
"size": 123,
"name": "something.doc",
"digestIdentifier": "fake-digest-identifier",
"content-type": "application/msword"
}
}
Expand All @@ -160,7 +161,7 @@ public void shouldOnlySendUpdatedContentMetadata()
{
UpdateNodeEvent event = new UpdateNodeEvent(NODE_ID, CREATE_OR_UPDATE, SOURCE_ID, TIMESTAMP)
.addContentInstruction(new ContentProperty(CONTENT_PROPERTY, null, null,
"application/msword", null, null));
"application/msword", null, null, "fake-digest-identifier"));

String expectedJson = """
[
Expand All @@ -173,6 +174,7 @@ public void shouldOnlySendUpdatedContentMetadata()
"cm:content": {
"file": {
"content-metadata": {
"digestIdentifier": "fake-digest-identifier",
"content-type": "application/msword"
}
}
Expand Down
Loading
Loading