Skip to content

Commit

Permalink
feat(#677): support for archiving entity with custom interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
novoj committed Nov 18, 2024
1 parent a50641b commit 5d394d7
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 7 deletions.
46 changes: 46 additions & 0 deletions evita_api/src/main/java/io/evitadb/api/proxy/WithScope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* 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.
* You may obtain a copy of the License at
*
* https://github.com/FgForrest/evitaDB/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.evitadb.api.proxy;

import io.evitadb.dataType.Scope;

import javax.annotation.Nonnull;

/**
* Interface can be implemented by client model classes that want to access scope information on the entity.
*
* @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023
*/
public interface WithScope {

/**
* Retrieves the scope of the entity within the EvitaDB system.
* The scope indicates whether the entity is active and resides in the live data set or is archived.
*
* @return the scope of the entity
*/
@Nonnull
Scope getScope();

}
45 changes: 45 additions & 0 deletions evita_api/src/main/java/io/evitadb/api/proxy/WithScopeEditor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* 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.
* You may obtain a copy of the License at
*
* https://github.com/FgForrest/evitaDB/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.evitadb.api.proxy;

import io.evitadb.dataType.Scope;

import javax.annotation.Nonnull;

/**
* Interface can be implemented by client model classes that want to modify scope information on the entity.
*
* @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023
*/
public interface WithScopeEditor extends WithScope {

/**
* Sets the scope of the entity in the EvitaDB system. The scope determines whether the entity is
* currently active and resides in the live data set or is archived.
*
* @param scope the scope to set for the entity; must not be null
*/
void setScope(@Nonnull Scope scope);

}
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 @@ -53,6 +53,7 @@ public class EntityContractAdvice implements Advice<SealedEntityProxy> {
GetLocalesMethodClassifier.INSTANCE,
GetEntitySchemaMethodClassifier.INSTANCE,
GetVersionMethodClassifier.INSTANCE,
GetScopeMethodClassifier.INSTANCE,
GetEntityTypeMethodClassifier.INSTANCE,
GetAttributeMethodClassifier.INSTANCE,
GetAssociatedDataMethodClassifier.INSTANCE,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* 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.
* You may obtain a copy of the License at
*
* https://github.com/FgForrest/evitaDB/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.evitadb.api.proxy.impl.entity;

import io.evitadb.api.proxy.WithScope;
import io.evitadb.api.proxy.impl.SealedEntityProxyState;
import one.edee.oss.proxycian.DirectMethodClassification;
import one.edee.oss.proxycian.util.ReflectionUtils;

/**
* Identifies methods that are used to get entity scope from an entity and provides their implementation.
*
* @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023
*/
public class GetScopeMethodClassifier extends DirectMethodClassification<Object, SealedEntityProxyState> {
/**
* We may reuse singleton instance since advice is stateless.
*/
public static final GetScopeMethodClassifier INSTANCE = new GetScopeMethodClassifier();

public GetScopeMethodClassifier() {
super(
"getScope",
(method, proxyState) -> {
// We are interested only in abstract methods without arguments
if (method.getParameterCount() > 0) {
return null;
}

if (ReflectionUtils.isMatchingMethodPresentOn(method, WithScope.class)) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> theState.entity().getScope();
}

// this method is not classified by this implementation
return null;
}
);
}

}
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 All @@ -25,12 +25,14 @@

import io.evitadb.api.EvitaSessionContract;
import io.evitadb.api.proxy.SealedEntityProxy;
import io.evitadb.api.proxy.WithScopeEditor;
import io.evitadb.api.proxy.impl.SealedEntityProxyState;
import io.evitadb.api.requestResponse.data.EntityContract;
import io.evitadb.api.requestResponse.data.InstanceEditor;
import io.evitadb.api.requestResponse.data.SealedInstance;
import io.evitadb.api.requestResponse.data.mutation.LocalMutation;
import io.evitadb.api.requestResponse.data.structure.EntityReference;
import io.evitadb.dataType.Scope;
import one.edee.oss.proxycian.MethodClassification;
import one.edee.oss.proxycian.PredicateMethodClassification;
import one.edee.oss.proxycian.recipe.Advice;
Expand Down Expand Up @@ -71,6 +73,7 @@ public class EntityBuilderAdvice implements Advice<SealedEntityProxy> {
upsertDeeplyViaMethodClassification(),
toMutationArrayMethodClassification(),
toMutationCollectionMethodClassification(),
setScopeMethodClassification(),
openForWriteMethodClassification(),
SetAttributeMethodClassifier.INSTANCE,
SetAssociatedDataMethodClassifier.INSTANCE,
Expand Down Expand Up @@ -233,6 +236,16 @@ private static PredicateMethodClassification<Object, Method, SealedEntityProxySt
);
}

@Nonnull
private static PredicateMethodClassification<Object, Method, SealedEntityProxyState> setScopeMethodClassification() {
return new PredicateMethodClassification<>(
"setScope",
(method, proxyState) -> ReflectionUtils.isMatchingMethodPresentOn(method, WithScopeEditor.class) && "setScope".equals(method.getName()),
(method, proxyState) -> method,
(proxy, method, args, methodContext, proxyState, invokeSuper) -> proxyState.entityBuilder().setScope((Scope) args[0])
);
}

@Override
public Class<SealedEntityProxy> getRequestedStateContract() {
return SealedEntityProxy.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import io.evitadb.api.requestResponse.data.SealedEntity;
import io.evitadb.api.requestResponse.data.structure.EntityReference;
import io.evitadb.core.Evita;
import io.evitadb.dataType.Scope;
import io.evitadb.dataType.data.ReflectionCachingBehaviour;
import io.evitadb.test.Entities;
import io.evitadb.test.EvitaTestSupport;
Expand Down Expand Up @@ -428,6 +429,7 @@ private static void assertProductBasicData(@Nonnull SealedEntity originalProduct
assertEquals(originalProduct.getPrimaryKey(), product.getId());
assertEquals(Entities.PRODUCT, product.getType());
assertEquals(TestEntity.PRODUCT, product.getEntityType());
assertEquals(Scope.LIVE, product.getScope());
}

private static void assertProductAttributes(@Nonnull SealedEntity originalProduct, @Nonnull ProductInterface product, @Nullable Locale locale) {
Expand Down Expand Up @@ -1124,6 +1126,46 @@ void x_deleteEntity(
);
}

@DisplayName("Should archive entity")
@Test
@UseDataSet(HUNDRED_PRODUCTS)
void x_archiveEntity(
EvitaContract evita,
List<SealedEntity> originalProducts
) {
final SealedEntity theProduct = originalProducts
.stream()
.filter(it -> it.getPrimaryKey() == 49)
.findFirst()
.orElseThrow();

evita.updateCatalog(
TEST_CATALOG,
session -> {
final SealedEntity productToArchive = session.queryOneSealedEntity(
query(
collection(Entities.PRODUCT),
filterBy(
entityPrimaryKeyInSet(49)
),
require(
entityFetchAll()
)
)
).orElseThrow();
assertEquals(theProduct, productToArchive);

productToArchive
.openForWrite()
.setScope(Scope.ARCHIVED)
.upsertVia(session);

assertTrue(session.getEntity(Entities.PRODUCT, 49, entityFetchAllContent()).isEmpty());
assertTrue(session.getEntity(Entities.PRODUCT, 49, new Scope[] { Scope.ARCHIVED }, entityFetchAllContent()).isPresent());
}
);
}

@DisplayName("Should delete entity with hierarchy")
@Test
@UseDataSet(HUNDRED_PRODUCTS)
Expand All @@ -1134,7 +1176,7 @@ void x_deleteEntityWithHierarchy(
final SealedEntity theCategory = originalCategories
.values()
.stream()
.max(Comparator.comparingInt(EntityContract::getPrimaryKey))
.max(Comparator.comparingInt(EntityContract::getPrimaryKeyOrThrowException))
.orElseThrow();

evita.updateCatalog(
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 All @@ -29,6 +29,7 @@
import io.evitadb.api.proxy.WithEntityContract;
import io.evitadb.api.proxy.WithEntitySchema;
import io.evitadb.api.proxy.WithLocales;
import io.evitadb.api.proxy.WithScope;
import io.evitadb.api.proxy.WithVersion;
import io.evitadb.api.requestResponse.data.EntityClassifier;
import io.evitadb.api.requestResponse.data.PriceContract;
Expand Down Expand Up @@ -56,7 +57,7 @@
* @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023
*/
@EntityRef(Entities.PRODUCT)
public interface ProductInterface extends EntityClassifier, WithEntityContract, WithEntitySchema, WithLocales, WithVersion {
public interface ProductInterface extends EntityClassifier, WithEntityContract, WithEntitySchema, WithLocales, WithVersion, WithScope {

@PrimaryKeyRef
int getId();
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 All @@ -26,6 +26,7 @@
import io.evitadb.api.AbstractHundredProductsFunctionalTest;
import io.evitadb.api.AbstractHundredProductsFunctionalTest.TestEnum;
import io.evitadb.api.proxy.WithEntityBuilder;
import io.evitadb.api.proxy.WithScopeEditor;
import io.evitadb.api.requestResponse.data.InstanceEditor;
import io.evitadb.api.requestResponse.data.PriceContract;
import io.evitadb.api.requestResponse.data.annotation.AssociatedDataRef;
Expand Down Expand Up @@ -54,7 +55,7 @@
*
* @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023
*/
public interface ProductInterfaceEditor extends ProductInterface, WithEntityBuilder, InstanceEditor<ProductInterface> {
public interface ProductInterfaceEditor extends ProductInterface, WithEntityBuilder, WithScopeEditor, InstanceEditor<ProductInterface> {

ProductInterfaceEditor setId(int id);

Expand Down

0 comments on commit 5d394d7

Please sign in to comment.