Skip to content

Commit

Permalink
Fix: Record Content-Type of Binary Fields Correctly in Events (#31451)
Browse files Browse the repository at this point in the history
The /dA syntax is: /dA/[content's id]/[binary file field variable name]
The /contentAsset syntax is: /contentAsset/image/[content's id]/[binary
file field variable name]

before the change we were just taking the Content's id

I am going to create a new issue for the /dotAsset/... url

### Proposed Changes
* No we are taking the content's id and the field name to look for the
right Content to be track


https://github.com/dotCMS/core/pull/31451/files#diff-9a756a0cf33f2fc28cfef619e51e6943ece96c251f3f7c98a77d3bb9e76b126aL88-R141
  • Loading branch information
freddyDOTCMS authored Feb 25, 2025
1 parent 55698fa commit 017e0f1
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.dotmarketing.portlets.contentlet.business.ContentletAPI;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import com.dotmarketing.portlets.fileassets.business.FileAssetAPI;
import com.dotmarketing.util.PageMode;
import com.liferay.util.StringPool;
import io.vavr.control.Try;

Expand Down Expand Up @@ -85,9 +86,8 @@ protected Optional<Contentlet> getFileAsset(String uri, Host host, Long language
final String actualUri = uri.substring(0, uri.lastIndexOf('.')) + ".scss";
return Optional.ofNullable(this.fileAssetAPI.getFileByPath(actualUri, host, languageId, true));
} else if (uri.startsWith("/dA") || uri.startsWith("/contentAsset") || uri.startsWith("/dotAsset")) {
final String[] split = uri.split(StringPool.FORWARD_SLASH);
final String id = uri.startsWith("/contentAsset") ? split[3] : split[2];
return getFileAsset(languageId, id);
final FieldNameIdentifier fieldNameIdentifier = getIdentifierAndFieldName(uri);
return getFileAsset(languageId, fieldNameIdentifier);
} else {
return Optional.ofNullable(this.fileAssetAPI.getFileByPath(uri, host, languageId, true));
}
Expand All @@ -96,15 +96,47 @@ protected Optional<Contentlet> getFileAsset(String uri, Host host, Long language
}
}

private Optional<Contentlet> getFileAsset(final Long languageId, final String id) throws DotDataException, DotSecurityException {
private static FieldNameIdentifier getIdentifierAndFieldName(String uri) {
final String[] split = uri.split(StringPool.FORWARD_SLASH);

return Optional.ofNullable(contentletAPI.findContentletByIdentifier(id, true, languageId,
APILocator.systemUser(), false));
final int idIndex = uri.startsWith("/contentAsset") ? 3 : 2;
final int fieldNameIndex = uri.startsWith("/contentAsset") ? 4 : 3;

return new FieldNameIdentifier(split[idIndex],
fieldNameIndex < split.length || !uri.startsWith("/dotAsset")? split[fieldNameIndex] : null);
}

private Optional<Contentlet> getFileAsset(final Long languageId, final FieldNameIdentifier fieldNameIdentifier)
throws DotDataException, DotSecurityException {

final Contentlet contentletByIdentifier = contentletAPI.findContentletByIdentifier(fieldNameIdentifier.identifier,
PageMode.get().showLive, languageId,
APILocator.systemUser(), false);

if (Objects.nonNull(fieldNameIdentifier.fieldName)) {
final String binaryFileId = contentletByIdentifier.getStringProperty(fieldNameIdentifier.fieldName);

return Optional.ofNullable(contentletAPI.findContentletByIdentifier(binaryFileId,
PageMode.get().showLive, languageId,
APILocator.systemUser(), false));
} else {
return Optional.ofNullable(contentletByIdentifier);
}
}

@Override
public boolean isAsync() {
return true;
}

private static class FieldNameIdentifier {
final String fieldName;
final String identifier;

FieldNameIdentifier(final String identifier, final String fieldName) {
this.fieldName = fieldName;
this.identifier = identifier;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package com.dotcms.analytics.track.collectors;

import com.dotcms.JUnit4WeldRunner;
import com.dotcms.LicenseTestUtil;
import com.dotcms.contenttype.model.field.Field;
import com.dotcms.contenttype.model.field.ImageField;
import com.dotcms.contenttype.model.field.TextField;
import com.dotcms.contenttype.model.type.ContentType;
import com.dotcms.datagen.*;
import com.dotcms.enterprise.cluster.ClusterFactory;
import com.dotcms.util.IntegrationTestInitService;
import com.dotmarketing.beans.Host;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.image.focalpoint.FocalPointAPITest;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import com.dotmarketing.portlets.folders.model.Folder;
import com.dotmarketing.portlets.languagesmanager.business.UniqueLanguageDataGen;
import com.dotmarketing.portlets.languagesmanager.model.Language;
import org.apache.commons.io.FileUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.enterprise.context.Dependent;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;

import static com.dotcms.analytics.track.collectors.Collector.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@Dependent
@RunWith(JUnit4WeldRunner.class)
public class FilesCollectorIntegrationTest {

@BeforeClass
public static void prepare() throws Exception {
// Setting web app environment
IntegrationTestInitService.getInstance().init();
}

/**
* Method to test: {@link FilesCollector#collect(CollectorContextMap, CollectorPayloadBean)}
* when:
* - Create a Content TYpe with an Image field, let called it 'contentTypeWithImageField'
* - Create a FileAsset pointing to an Image
* - Create a Contentlet using the 'contentTypeWithImageField' ContentType created in the first step
* - Try to collect the Analytics data pretending that the Image was hit using a
* /dA/[contentTypeWithImageField Content's id]/{contentTypeWithImageField image field variable name}
*
* Should: collect the data using the Image Contentlet not the 'contentTypeWithImageField' Contentlet
*/
@Test
public void registerdAUriInAnalytics() throws IOException, DotDataException, DotSecurityException {
final Host host = new SiteDataGen().nextPersisted();
final Language language = new UniqueLanguageDataGen().nextPersisted();

final FilesCollector filesCollector = new FilesCollector();

final CollectorContextMap collectorContextMap = mock(CollectorContextMap.class);
final CollectorPayloadBean collectorPayloadBean = new ConcurrentCollectorPayloadBeanWithBaseMap(new HashMap<>());

final Field imageField = new FieldDataGen().type(ImageField.class).next();

final Folder imageFolder = new FolderDataGen().site(host).nextPersisted();

File tempFile = File.createTempFile("contentWithImageBundleTest", ".jpg");
URL url = FocalPointAPITest.class.getResource("/images/test.jpg");
File testImage = new File(url.getFile());
FileUtils.copyFile(testImage, tempFile);

final Contentlet imageFileAsset = new FileAssetDataGen(tempFile)
.host(host)
.languageId(language.getId())
.folder(imageFolder).nextPersisted();

final ContentType contentTypeWithImageField = new ContentTypeDataGen().host(host).field(imageField).nextPersisted();
final Contentlet contentletWithImage = new ContentletDataGen(contentTypeWithImageField)
.host(host)
.setProperty(imageField.variable(), imageFileAsset.getIdentifier())
.languageId(language.getId())
.nextPersisted();

ContentletDataGen.publish(contentletWithImage);
ContentletDataGen.publish(imageFileAsset);

final String uri = String.format("/dA/%s/%s", contentletWithImage.getIdentifier(), imageField.variable());

when(collectorContextMap.get(CollectorContextMap.URI)).thenReturn(uri);
when(collectorContextMap.get(CollectorContextMap.HOST)).thenReturn(host.getIdentifier());
when(collectorContextMap.get(CollectorContextMap.CURRENT_HOST)).thenReturn(host);
when(collectorContextMap.get(CollectorContextMap.LANG_ID)).thenReturn(language.getId());
when(collectorContextMap.get(CollectorContextMap.LANG)).thenReturn(language.getLanguageCode());

CollectorPayloadBean collect = filesCollector.collect(collectorContextMap, collectorPayloadBean);

assertEquals(collectorPayloadBean.get(EVENT_TYPE), EventType.FILE_REQUEST.getType());
final HashMap<String, String> fileObjectFromPayload = (HashMap<String, String>) collectorPayloadBean.get(OBJECT);

final ContentType imageContentType = imageFileAsset.getContentType();

assertEquals( imageFileAsset.getIdentifier(), fileObjectFromPayload.get(ID));
assertEquals(imageFileAsset.getTitle(), fileObjectFromPayload.get(TITLE));
assertEquals( uri, fileObjectFromPayload.get(URL));
assertEquals(imageContentType.id(), fileObjectFromPayload.get(CONTENT_TYPE_ID));
assertEquals(imageContentType.name(), fileObjectFromPayload.get(CONTENT_TYPE_NAME));
assertEquals(imageContentType.variable(), fileObjectFromPayload.get(CONTENT_TYPE_VAR_NAME));
assertEquals(imageContentType.baseType().name(), fileObjectFromPayload.get(BASE_TYPE));

}

/**
* Method to test: {@link FilesCollector#collect(CollectorContextMap, CollectorPayloadBean)}
* when:
* - Create a Content TYpe with an Image field, let called it 'contentTypeWithImageField'
* - Create a FileAsset pointing to an Image
* - Create a Contentlet using the 'contentTypeWithImageField' ContentType created in the first step
* - Try to collect the Analytics data pretending that the Image was hit using a
* /contentAsset/image/[contentTypeWithImageField Content's id]/{contentTypeWithImageField image field variable name}
*
* Should: collect the data using the Image Contentlet not the 'contentTypeWithImageField' Contentlet
*/
@Test
public void registerContentAssetsUriInAnalytics() throws IOException, DotDataException, DotSecurityException {
final Host host = new SiteDataGen().nextPersisted();
final Language language = new UniqueLanguageDataGen().nextPersisted();

final FilesCollector filesCollector = new FilesCollector();

final CollectorContextMap collectorContextMap = mock(CollectorContextMap.class);
final CollectorPayloadBean collectorPayloadBean = new ConcurrentCollectorPayloadBeanWithBaseMap(new HashMap<>());

final Field fieldTitle = new FieldDataGen().type(TextField.class).name("title").next();
final Field fieldImage = new FieldDataGen().type(ImageField.class).name("image").next();

final ContentType contentType = new ContentTypeDataGen().field(fieldTitle).field(fieldImage).nextPersisted();

final Language imageFileLanguage = new UniqueLanguageDataGen().nextPersisted();
final Folder imageFolder = new FolderDataGen().site(host).nextPersisted();

File tempFile = File.createTempFile("contentWithImageBundleTest", ".jpg");
URL url = FocalPointAPITest.class.getResource("/images/test.jpg");
File testImage = new File(url.getFile());
FileUtils.copyFile(testImage, tempFile);

final Contentlet imageFileAsset = new FileAssetDataGen(tempFile)
.host(host)
.languageId(imageFileLanguage.getId())
.folder(imageFolder).nextPersisted();

final Field imageField = new FieldDataGen().type(ImageField.class).next();
final ContentType contentTypeWithImageField = new ContentTypeDataGen().host(host).field(imageField).nextPersisted();
final Contentlet contentletWithImage = new ContentletDataGen(contentTypeWithImageField)
.host(host)
.setProperty(imageField.variable(), imageFileAsset.getIdentifier())
.languageId(language.getId())
.nextPersisted();

final String uri = String.format("/contentAsset/image/%s/%s", contentletWithImage.getIdentifier(), fieldImage.variable());

when(collectorContextMap.get(CollectorContextMap.URI)).thenReturn(uri);
when(collectorContextMap.get(CollectorContextMap.HOST)).thenReturn(host.getIdentifier());
when(collectorContextMap.get(CollectorContextMap.CURRENT_HOST)).thenReturn(host);
when(collectorContextMap.get(CollectorContextMap.LANG_ID)).thenReturn(language.getId());
when(collectorContextMap.get(CollectorContextMap.LANG)).thenReturn(language.getLanguageCode());

CollectorPayloadBean collect = filesCollector.collect(collectorContextMap, collectorPayloadBean);

assertEquals(collectorPayloadBean.get(EVENT_TYPE), EventType.FILE_REQUEST.getType());
final HashMap<String, String> fileObject = (HashMap<String, String>) collectorPayloadBean.get(OBJECT);

final ContentType imageContentType = imageFileAsset.getContentType();

assertEquals(imageFileAsset.getIdentifier(), fileObject.get(ID));
assertEquals(imageFileAsset.getTitle(), fileObject.get(TITLE));
assertEquals(uri, fileObject.get(URL));
assertEquals(imageContentType.id(), fileObject.get(CONTENT_TYPE_ID));
assertEquals(imageContentType.name(), fileObject.get(CONTENT_TYPE_NAME));
assertEquals(imageContentType.variable(), fileObject.get(CONTENT_TYPE_VAR_NAME));
assertEquals(imageContentType.baseType().name(), fileObject.get(BASE_TYPE));

}

@Test
public void registerDotAssetsUriInAnalytics() throws IOException, DotDataException, DotSecurityException {
throw new DotRuntimeException("test");
}
}

0 comments on commit 017e0f1

Please sign in to comment.