Skip to content

Commit fa9281d

Browse files
fix(import): Fix issue when importing contents with relationship due to language (dotCMS#31962)
Problem During the import process, an error was occurring when a contentlet was related to another contentlet that had versions in multiple languages. The issue was that the code treats a content with different languages as distinct entities — it incorrectly assumed they were different contents, although the content is the same. Fix When validating if the content is trying to relate to more contents that the one already has, we group by identifier the array of the contents related, this way although the content has more than one language versions, it would be interpreted as an unique relation. So in the scenario the issue shows it won't throw an error. If we have a case in where the content has more than one relation with the same language, then we are going to get the error. --------- Co-authored-by: Jose Castro <[email protected]>
1 parent 8ee48f3 commit fa9281d

File tree

2 files changed

+102
-9
lines changed

2 files changed

+102
-9
lines changed

dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImpl.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@
219219
import java.util.Optional;
220220
import java.util.Set;
221221
import java.util.concurrent.ConcurrentHashMap;
222+
import java.util.function.Function;
222223
import java.util.function.Supplier;
223224
import java.util.regex.Pattern;
224225
import java.util.stream.Collectors;
@@ -8330,11 +8331,21 @@ private void validateRelationships(final Contentlet contentlet,
83308331
.getRelationTypeValue() + "] is required.");
83318332
cve.addRequiredRelationship(relationship, contentsInRelationship);
83328333
}
8334+
//grouping by id to avoid duplicate contents due to different languages
8335+
List<Contentlet> contentsInRelationshipSameLanguage = new ArrayList<>(contentsInRelationship.stream()
8336+
.collect(Collectors.toMap(
8337+
Contentlet::getIdentifier,
8338+
Function.identity(),
8339+
(existing, replacement) -> existing
8340+
))
8341+
.values());
8342+
83338343
// If there's a 1-N relationship and the child content is
83348344
// trying to relate to one more parent...
8345+
83358346
if (relationship.getCardinality()
83368347
== RELATIONSHIP_CARDINALITY.ONE_TO_MANY.ordinal()
8337-
&& contentsInRelationship.size() > 1) {
8348+
&& contentsInRelationshipSameLanguage.size() > 1) {
83388349
final StringBuilder error = new StringBuilder();
83398350
error.append("ERROR! Child content [").append(contentletId)
83408351
.append("] is already related to another parent content [");

dotcms-integration/src/test/java/com/dotcms/util/ImportUtilTest.java

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,14 @@
2323
import com.dotcms.contenttype.model.type.BaseContentType;
2424
import com.dotcms.contenttype.model.type.ContentType;
2525
import com.dotcms.contenttype.transform.contenttype.StructureTransformer;
26-
import com.dotcms.datagen.ContentTypeDataGen;
27-
import com.dotcms.datagen.ContentletDataGen;
28-
import com.dotcms.datagen.FieldDataGen;
29-
import com.dotcms.datagen.FolderDataGen;
30-
import com.dotcms.datagen.SiteDataGen;
31-
import com.dotcms.datagen.TemplateDataGen;
32-
import com.dotcms.datagen.TestDataUtils;
33-
import com.dotcms.datagen.TestUserUtils;
26+
import com.dotcms.datagen.*;
3427
import com.dotcms.mock.request.MockAttributeRequest;
3528
import com.dotcms.mock.request.MockHeaderRequest;
3629
import com.dotcms.mock.request.MockHttpRequestIntegrationTest;
3730
import com.dotcms.mock.request.MockSessionRequest;
3831
import com.dotcms.repackage.com.csvreader.CsvReader;
3932
import com.dotcms.uuid.shorty.ShortyIdAPI;
33+
import com.dotcms.variant.VariantAPI;
4034
import com.dotmarketing.beans.Host;
4135
import com.dotmarketing.beans.Identifier;
4236
import com.dotmarketing.beans.Permission;
@@ -2691,6 +2685,94 @@ public void importFile_importBinaryFieldUsingURLWithRequiredBinaryField_success(
26912685
}
26922686
}
26932687
}
2688+
/**
2689+
* Method to test: This test tries the {@link ImportUtil#importFile}
2690+
* Given Scenario: A parent content type related to a child content type that has two versions in two different languages
2691+
* ExpectedResult: The importer should return without errors, so content will be ready to be imported.
2692+
*/
2693+
@Test
2694+
public void importPreviewRelationshipLanguageTest() throws DotDataException, DotSecurityException, IOException {
2695+
//Creates content types
2696+
ContentType parentContentType = null;
2697+
ContentType childContentType = null;
2698+
2699+
HashMap<String, List<String>> results;
2700+
CsvReader csvreader;
2701+
Reader reader;
2702+
String[] csvHeaders;
2703+
final int cardinality = RELATIONSHIP_CARDINALITY.ONE_TO_MANY.ordinal();
2704+
2705+
final Language language_1 = new LanguageDataGen().nextPersisted();
2706+
final Language language_2 = new LanguageDataGen().nextPersisted();
2707+
2708+
try {
2709+
final Relationship relationship;
2710+
parentContentType = createTestContentType("parentContentType", "parentContentType" + new Date().getTime());
2711+
childContentType = createTestContentType("childContentType", "childContentType" + new Date().getTime());
2712+
2713+
2714+
com.dotcms.contenttype.model.field.Field field = FieldBuilder.builder(RelationshipField.class).name("testRelationship")
2715+
.variable("testRelationship")
2716+
.contentTypeId(parentContentType.id()).values(String.valueOf(cardinality))
2717+
.relationType(childContentType.variable()).build();
2718+
2719+
field = fieldAPI.save(field, user);
2720+
relationship = relationshipAPI.byTypeValue(
2721+
parentContentType.variable() + StringPool.PERIOD + field.variable());
2722+
2723+
2724+
//Creates child contentlet
2725+
final Contentlet childContentlet = new ContentletDataGen(childContentType.id())
2726+
.languageId(language_1.getId())
2727+
.setProperty(TITLE_FIELD_NAME, "child contentlet")
2728+
.setProperty(BODY_FIELD_NAME, "child contentlet").nextPersisted();
2729+
2730+
ContentletDataGen.createNewVersion(childContentlet, VariantAPI.DEFAULT_VARIANT, language_2, null);
2731+
2732+
//Creates parent contentlet
2733+
Contentlet parentContentlet = new ContentletDataGen(parentContentType.id())
2734+
.languageId(language_1.getId())
2735+
.setProperty(TITLE_FIELD_NAME, "parent contentlet")
2736+
.setProperty(BODY_FIELD_NAME, "parent contentlet").next();
2737+
2738+
parentContentlet = contentletAPI.checkin(parentContentlet,
2739+
Map.of(relationship, list(childContentlet)),
2740+
user, false);
2741+
2742+
reader = createTempFile(
2743+
"identifier, languageCode, countryCode, " + TITLE_FIELD_NAME + ", " + BODY_FIELD_NAME
2744+
+ "\r\n"
2745+
+ parentContentlet.getIdentifier() + ",en, US, Test1_edited, " + "\r\n" );
2746+
csvreader = new CsvReader(reader);
2747+
csvreader.setSafetySwitch(false);
2748+
csvHeaders = csvreader.getHeaders();
2749+
2750+
int languageCodeHeaderColumn = 0;
2751+
int countryCodeHeaderColumn = 1;
2752+
2753+
2754+
results = ImportUtil.importFile(0L, defaultSite.getInode(), parentContentType.inode(),
2755+
new String[]{}, true, true, user, language_1.getId(), csvHeaders,
2756+
csvreader, languageCodeHeaderColumn, countryCodeHeaderColumn, reader,
2757+
schemeStepActionResult1.getAction().getId(),getHttpRequest());
2758+
2759+
validate(results, true, false, true);
2760+
2761+
assertEquals(results.get("errors").size(), 0);
2762+
}finally {
2763+
try {
2764+
if (parentContentType != null) {
2765+
contentTypeApi.delete(parentContentType);
2766+
}
2767+
2768+
if (childContentType != null) {
2769+
contentTypeApi.delete(childContentType);
2770+
}
2771+
} catch (Exception e) {
2772+
Logger.error("Error deleting content type", e);
2773+
}
2774+
}
2775+
}
26942776

26952777

26962778
/**

0 commit comments

Comments
 (0)