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

fix(#596): Referenced entity bodies are not returned for hierarchically fetched parents #597

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 @@ -694,8 +694,7 @@ private static SealedEntity replaceWithSealedEntities(
.orElse(EntityClassifierWithParent.CONCEALED_ENTITY);

return ServerEntityDecorator.decorate(
entityDecorator.getDelegate(),
entityDecorator.getSchema(),
entityDecorator,
enrichedParentEntity,
entityDecorator.getLocalePredicate(),
new HierarchySerializablePredicate(true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,34 @@ public static ServerEntityDecorator decorate(
);
}

/**
* Method allows creating the entityDecorator object with up-to-date schema definition. Data of the entity are kept
* untouched.
*/
@Nonnull
public static ServerEntityDecorator decorate(
@Nonnull ServerEntityDecorator entity,
@Nullable EntityClassifierWithParent parentEntity,
@Nonnull LocaleSerializablePredicate localePredicate,
@Nonnull HierarchySerializablePredicate hierarchyPredicate,
@Nonnull AttributeValueSerializablePredicate attributePredicate,
@Nonnull AssociatedDataValueSerializablePredicate associatedDataValuePredicate,
@Nonnull ReferenceContractSerializablePredicate referencePredicate,
@Nonnull PriceContractSerializablePredicate pricePredicate,
@Nonnull OffsetDateTime alignedNow,
int ioFetchCount,
int ioFetchedBytes
) {
return new ServerEntityDecorator(
entity, parentEntity,
localePredicate, hierarchyPredicate,
attributePredicate, associatedDataValuePredicate,
referencePredicate, pricePredicate,
alignedNow,
ioFetchCount, ioFetchedBytes
);
}

/**
* Method allows to create copy of the entity object with up-to-date schema definition. Data of the original
* entity are kept untouched.
Expand Down Expand Up @@ -165,24 +193,26 @@ public ServerEntityDecorator(
}

public ServerEntityDecorator(
@Nonnull ServerEntityDecorator decorator,
@Nonnull ServerEntityDecorator delegate,
@Nullable EntityClassifierWithParent parentEntity,
@Nonnull LocaleSerializablePredicate localePredicate,
@Nonnull HierarchySerializablePredicate hierarchyPredicate,
@Nonnull AttributeValueSerializablePredicate attributePredicate,
@Nonnull AssociatedDataValueSerializablePredicate associatedDataPredicate,
@Nonnull ReferenceContractSerializablePredicate referencePredicate,
@Nonnull PriceContractSerializablePredicate pricePredicate,
@Nonnull OffsetDateTime alignedNow
@Nonnull OffsetDateTime alignedNow,
int ioFetchCount,
int ioFetchedBytes
) {
super(
decorator, parentEntity,
delegate, parentEntity,
localePredicate, hierarchyPredicate, attributePredicate, associatedDataPredicate,
referencePredicate, pricePredicate,
alignedNow
);
this.ioFetchCount = decorator.getIoFetchCount();
this.ioFetchedBytes = decorator.getIoFetchedBytes();
this.ioFetchCount = ioFetchCount;
this.ioFetchedBytes = ioFetchedBytes;
}

public ServerEntityDecorator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* Copyright (c) 2023
* Copyright (c) 2023-2024
*
* Licensed under the Business Source License, Version 1.1 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -72,11 +72,20 @@ DataCarrier setUp(Evita evita) {
return primaryKey == 0 ? null : primaryKey;
};

dataGenerator.generateEntities(
dataGenerator.getSamplePriceListSchema(session),
randomEntityPicker,
SEED
)
.limit(4)
.forEach(session::upsertEntity);

final List<EntityReference> storedCategories = dataGenerator.generateEntities(
dataGenerator.getSampleCategorySchema(
session,
builder -> {
builder
.withReferenceToEntity(Entities.PRICE_LIST, Entities.PRICE_LIST, Cardinality.ZERO_OR_ONE)
/* here we define set of associated data, that can be stored along with entity */
.withAssociatedData(ASSOCIATED_DATA_REFERENCED_FILES, ReferencedFileSet.class)
.withAssociatedData(ASSOCIATED_DATA_LABELS, Labels.class)
Expand All @@ -91,14 +100,6 @@ DataCarrier setUp(Evita evita) {
.map(session::upsertEntity)
.toList();

dataGenerator.generateEntities(
dataGenerator.getSamplePriceListSchema(session),
randomEntityPicker,
SEED
)
.limit(4)
.forEach(session::upsertEntity);

final List<EntityReference> storedStores = dataGenerator.generateEntities(
dataGenerator.getSampleStoreSchema(
session,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* Copyright (c) 2023
* Copyright (c) 2023-2024
*
* Licensed under the Business Source License, Version 1.1 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -3248,6 +3248,66 @@ void shouldReturnDirectHierarchyParentsUpToLevelTwo(Evita evita, Hierarchy categ
);
}

@DisplayName("Should return hierarchy parent entity references with bodies stopping at level two")
@UseDataSet(HUNDRED_PRODUCTS)
@Test
void shouldReturnDirectHierarchyParentsWithBodiesUpToLevelTwo(Evita evita, Hierarchy categoryHierarchy) {
evita.queryCatalog(
TEST_CATALOG,
session -> {
final HierarchyItem theChild = categoryHierarchy.getRootItems()
.stream()
.flatMap(it -> categoryHierarchy.getAllChildItems(it.getCode()).stream())
.max(Comparator.comparingInt(HierarchyItem::getLevel))
.orElseThrow();
final int theChildPk = Integer.parseInt(theChild.getCode());
final int theParentPk = Integer.parseInt(categoryHierarchy.getParentItem(theChild.getCode()).getCode());

final EvitaResponse<SealedEntity> categoryByPk = session.querySealedEntity(
query(
collection(Entities.CATEGORY),
filterBy(
entityPrimaryKeyInSet(theChildPk)
),
require(
entityFetch(
hierarchyContent(
stopAt(level(2)),
entityFetch(
attributeContentAll(),
referenceContent(Entities.PRICE_LIST, entityFetchAll())
)
)
)
)
)
);
assertEquals(1, categoryByPk.getRecordData().size());
assertEquals(1, categoryByPk.getTotalRecordCount());

final SealedEntity returnedEntity = categoryByPk.getRecordData().get(0);
assertEquals(theParentPk, returnedEntity.getParentEntity().orElseThrow().getPrimaryKey());

boolean atLeastOnPriceListFound = false;
Optional<EntityClassifierWithParent> parentEntityRef = returnedEntity.getParentEntity();
while (parentEntityRef.isPresent()) {
final EntityClassifierWithParent parentEntity = parentEntityRef.get();
assertInstanceOf(SealedEntity.class, parentEntity);
final Collection<ReferenceContract> references = ((SealedEntity) parentEntity).getReferences(Entities.PRICE_LIST);
if (!references.isEmpty()) {
atLeastOnPriceListFound = true;
assertEquals(1, references.size());
assertTrue(references.iterator().next().getReferencedEntity().isPresent());
parentEntityRef = parentEntity.getParentEntity();
}
}
assertTrue(atLeastOnPriceListFound, "At least one price list should be found in the hierarchy");

return null;
}
);
}

@DisplayName("Should return hierarchy parent entity references stopping at distance one")
@UseDataSet(HUNDRED_PRODUCTS)
@Test
Expand Down Expand Up @@ -3876,6 +3936,81 @@ void shouldReturnProductHierarchyParentEntitiesUpToLevelTwo(Evita evita, Hierarc
);
}

@DisplayName("Should return product hierarchy parent sealed entities stopping at level two")
@UseDataSet(HUNDRED_PRODUCTS)
@Test
void shouldReturnProductHierarchyParentEntitiesWithBodiesUpToLevelTwo(Evita evita, Hierarchy categoryHierarchy, Map<Integer, SealedEntity> originalCategories) {
evita.queryCatalog(
TEST_CATALOG,
session -> {
final HierarchyItem theChild = categoryHierarchy.getRootItems()
.stream()
.flatMap(it -> categoryHierarchy.getAllChildItems(it.getCode()).stream())
.max(Comparator.comparingInt(HierarchyItem::getLevel))
.orElseThrow();
final int theChildPk = Integer.parseInt(theChild.getCode());
final int theParentPk = Integer.parseInt(categoryHierarchy.getParentItem(theChild.getCode()).getCode());

final EvitaResponse<SealedEntity> products = session.querySealedEntity(
query(
collection(Entities.PRODUCT),
filterBy(
hierarchyWithin(Entities.CATEGORY, entityPrimaryKeyInSet(theChildPk))
),
require(
entityFetch(
referenceContent(
Entities.CATEGORY,
entityFetch(
hierarchyContent(
stopAt(level(2)),
entityFetch(
attributeContentAll(),
referenceContent(Entities.PRICE_LIST, entityFetchAll())
)
)
)
)
)
)
)
);
assertFalse(products.getRecordData().isEmpty());
assertTrue(products.getTotalRecordCount() > 0);

final ReferenceContract categoryReference = products.getRecordData().get(0).getReference(Entities.CATEGORY, theChildPk).orElseThrow();
final SealedEntity referencedCategory = categoryReference.getReferencedEntity().orElseThrow();
assertEquals(theParentPk, referencedCategory.getParentEntity().orElseThrow().getPrimaryKey());

boolean atLeastOnPriceListFound = false;
for (SealedEntity returnedEntity : products.getRecordData()) {
final Collection<ReferenceContract> referencedCategories = returnedEntity.getReferences(Entities.CATEGORY);
for (ReferenceContract category : referencedCategories) {
final Optional<SealedEntity> referencedEntity = category.getReferencedEntity();
if (referencedEntity.isPresent()) {
Optional<EntityClassifierWithParent> parentEntityRef = referencedEntity.get().getParentEntity();
while (parentEntityRef.isPresent()) {
final EntityClassifierWithParent parentEntity = parentEntityRef.get();
assertInstanceOf(SealedEntity.class, parentEntity);
final Collection<ReferenceContract> references = ((SealedEntity) parentEntity).getReferences(Entities.PRICE_LIST);
if (!references.isEmpty()) {
atLeastOnPriceListFound = true;
assertEquals(1, references.size());
assertTrue(references.iterator().next().getReferencedEntity().isPresent());
parentEntityRef = parentEntity.getParentEntity();
}
}
}

}
}

assertTrue(atLeastOnPriceListFound, "At least one price list should be found in the hierarchy");
return null;
}
);
}

@DisplayName("Should return product hierarchy parent sealed entities stopping at distance one")
@UseDataSet(HUNDRED_PRODUCTS)
@Test
Expand Down