Skip to content

Commit

Permalink
Adjusts abstract BaSyxStorageAPI for MongoDB Backend (#319)
Browse files Browse the repository at this point in the history
Signed-off-by: Jannis Jung <[email protected]>
Co-authored-by: Daniel Espen <[email protected]>
  • Loading branch information
jannisjung and Daespen authored Jul 11, 2023
1 parent 0835bce commit eee2ec2
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 37 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,5 @@ local.properties

.idea
*.iml
.moquette_uuid
file_sme.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@

import java.io.File;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.basyx.aas.metamodel.api.IAssetAdministrationShell;
import org.eclipse.basyx.aas.metamodel.map.descriptor.AASDescriptor;
import org.eclipse.basyx.aas.metamodel.map.descriptor.ModelDescriptor;
import org.eclipse.basyx.submodel.metamodel.api.ISubmodel;
import org.eclipse.basyx.submodel.metamodel.api.qualifier.IIdentifiable;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.ISubmodelElement;
Expand All @@ -56,6 +60,13 @@ public abstract class BaSyxStorageAPI<T> implements IBaSyxStorageAPI<T> {
protected final String COLLECTION_NAME;
protected final Class<T> TYPE;

/**
* The default constructor is primarily used for mocking purposes of the class.
*/
BaSyxStorageAPI() {
this(null, null);
}

/**
*
* @param collectionName
Expand All @@ -70,20 +81,27 @@ public BaSyxStorageAPI(String collectionName, Class<T> type) {
}

/**
* DISCLAIMER: Currently only supports to extract keys from IIdentifiables.
* Helper method that extracts a key for persistence storage requests from an
* object.
* DISCLAIMER: Currently only supports to extract keys from IIdentifiables and
* ModelDescriptors.
* Helper method that extracts a key for persistence storage
* requests from an object.
*
* @param obj
* An object that contains a key that can be used to find the
* persisted version of the object.
* @return The key
*/
protected String getKey(T obj) {
if (!(obj instanceof IIdentifiable)) {
throw new IllegalArgumentException("Can only extract a key from a object of type " + IIdentifiable.class.getName());
if (!(obj instanceof IIdentifiable || obj instanceof ModelDescriptor)) {
throw new IllegalArgumentException("Can only extract a key from a object of types " + IIdentifiable.class.getName() + " or " + ModelDescriptor.class.getName());
}

if (obj instanceof ModelDescriptor) {
return ((ModelDescriptor) obj).getIdentifier().getId();
} else {
return ((IIdentifiable) obj).getIdentification().getId();

}
return ((IIdentifiable) obj).getIdentification().getId();
}

/**
Expand All @@ -100,12 +118,22 @@ protected String getKey(T obj) {
*/
public abstract T rawRetrieve(String key);

public abstract Collection<T> rawRetrieveAll();

public abstract File getFile(String key, String parentKey, Map<String, Object> objMap);

public abstract String writeFile(String key, String parentKey, InputStream fileStream, ISubmodelElement submodelElement);

public abstract void deleteFile(Submodel submodel, String idShort);

/**
* This Method shall return an implementation specific connection object which
* can be used to implement more advanced operations on the storage.
*
* @return An object that can be used for direct storage access
*/
public abstract Object getStorageConnection();

/**
* Returns a Object that was originally retrieved from the abstract method
* {@code rawRetrieve}. If the object to be returned is a submodel type, it will
Expand All @@ -121,6 +149,20 @@ public T retrieve(String key) {
return retrieved;
}

@SuppressWarnings("unchecked")
@Override
public Collection<T> retrieveAll() {
Collection<T> retrieves = rawRetrieveAll();
if (!CollectionUtils.isEmpty(retrieves) && isSubmodelType(getElementClass(retrieves))) {
return (Collection<T>) retrieves.stream().map(submodel -> handleRetrievedSubmodel((Submodel) submodel)).collect(Collectors.toList());
}
return retrieves;
}

private Class<? extends Object> getElementClass(Collection<T> collection) {
return collection.iterator().next().getClass();
}

/**
* Helper to bring the SubmodelElements a retrieved SubmodelObject to the
* correct Type of ISubmodelElements. Background: SubmodelsElements are
Expand All @@ -137,7 +179,7 @@ public T retrieve(String key) {
@SuppressWarnings("unchecked")
protected Submodel handleRetrievedSubmodel(Submodel retrieved) {
Map<String, Map<String, Object>> elementMaps = (Map<String, Map<String, Object>>) retrieved.get(Submodel.SUBMODELELEMENT);
Map<String, ISubmodelElement> elements = forceToISubmodelElements(elementMaps);
Map<String, ISubmodelElement> elements = enforceISubmodelElements(elementMaps);
retrieved.put(Submodel.SUBMODELELEMENT, elements);
return retrieved;
}
Expand All @@ -154,7 +196,7 @@ protected Submodel handleRetrievedSubmodel(Submodel retrieved) {
* can be get with {@code submodel.get(Submodel.SUBMODELELEMENT)}))
* @return A map in the expected form of {@code Map<String, ISubmodelElement>}
*/
private Map<String, ISubmodelElement> forceToISubmodelElements(Map<String, Map<String, Object>> submodelElementObjectMap) {
private Map<String, ISubmodelElement> enforceISubmodelElements(Map<String, Map<String, Object>> submodelElementObjectMap) {
Map<String, ISubmodelElement> elements = new HashMap<>();

submodelElementObjectMap.forEach((idShort, elementMap) -> {
Expand All @@ -164,32 +206,23 @@ private Map<String, ISubmodelElement> forceToISubmodelElements(Map<String, Map<S
return elements;
}

/*
* Not yet tested
*/
protected boolean isSubmodelType(Class<?> type) {
return ISubmodel.class.isAssignableFrom(type);
}

/*
* Not yet tested
*/
protected boolean isShellType(Class<?> type) {
return IAssetAdministrationShell.class.isAssignableFrom(type);
}

/*
* Not yet tested
*/
protected boolean isAASDescriptorType(Class<?> type) {
return AASDescriptor.class.isAssignableFrom(type);
}

/*
* Not yet tested
*/
protected boolean isBaSyxType(Class<?> type) {
return (isShellType(type) || isSubmodelType(type) || isAASDescriptorType(type));
}

public String getCollectionName() {
return COLLECTION_NAME;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,12 @@ public class FeatureNotImplementedException extends RuntimeException {
*/
private static final long serialVersionUID = 1L;

public FeatureNotImplementedException() {
super();
}

public FeatureNotImplementedException(final String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.Collection;

import org.eclipse.basyx.extensions.internal.storage.BaSyxStorageAPI;
Expand All @@ -49,44 +50,103 @@
*/
public abstract class BaSyxStorageAPISuite {

private static final Identifier SUBMODEL_IDENTIFIER = new Identifier(IdentifierType.CUSTOM, "testSubmodelIidentifier");
protected static Submodel testType = new Submodel("testSubmodel", SUBMODEL_IDENTIFIER);
private BaSyxStorageAPI<Submodel> storageAPI;
private final Identifier SUBMODEL_IDENTIFIER = new Identifier(IdentifierType.CUSTOM, "testSubmodelIidentifier");
protected Submodel testSubmodel = new Submodel("testSubmodel", SUBMODEL_IDENTIFIER);
protected BaSyxStorageAPI<Submodel> storageAPI;

protected abstract BaSyxStorageAPI<Submodel> getStorageAPI();

/**
* This Storage API is used to approve data persistency. Therefore this method
* shall not return the very same object returned by {@link #getStorageAPI()}
* but still connect to the same storage.
*
* @return A BaSyxStorageAPI that is not same as returned by
* {@link #getStorageAPI()}
*/
protected abstract BaSyxStorageAPI<Submodel> getSecondStorageAPI();

@Before
public void setUp() {
storageAPI = getStorageAPI();
}

@After
public void cleanUp() {
storageAPI.delete(testType.getIdentification().getId());
storageAPI.delete(testSubmodel.getIdentification().getId());
}

@Test
public void retrieve() {
storageAPI.createOrUpdate(testType);
Submodel actual = storageAPI.retrieve(testType.getIdentification().getId());
assertEquals(testType, actual);
public void createOrUpdateAndretrieve() {
storageAPI.createOrUpdate(testSubmodel);
Submodel actual = storageAPI.retrieve(testSubmodel.getIdentification().getId());
assertEquals(testSubmodel, actual);
}

@Test
public void retrieveAll() {
Submodel[] testSubmodels = createTestSubmodels();
uploadMultiple(testSubmodels);

Collection<Submodel> retrieves = storageAPI.retrieveAll();
for (Submodel submodel : testSubmodels) {
assertTrue(retrieves.contains(submodel));
}
}

private Submodel[] createTestSubmodels() {
Submodel[] testTypes = new Submodel[3];
Arrays.setAll(testTypes, i -> new Submodel(testSubmodel.getIdShort() + i, new Identifier(IdentifierType.CUSTOM, "test" + i)));
return testTypes;
}

private void uploadMultiple(Submodel[] testTypes) {
for (Submodel submodel : testTypes) {
storageAPI.createOrUpdate(submodel);
}
}

@Test
public void update() {
storageAPI.createOrUpdate(testType);
testType.setIdShort("updated");
Submodel actual = storageAPI.createOrUpdate(testType);
storageAPI.createOrUpdate(testSubmodel);
testSubmodel.setIdShort("updated");
Submodel actual = storageAPI.update(testSubmodel, testSubmodel.getIdentification().getId());

assertEquals(testType, actual);
assertEquals(testSubmodel, actual);
}

@Test
public void updateShallServeAsCreate() {
Submodel actual = storageAPI.update(testSubmodel, testSubmodel.getIdentification().getId());

assertEquals(testSubmodel, actual);
}

@Test
public void delete() {
System.out.println(testType);
storageAPI.createOrUpdate(testType);
assertTrue(storageAPI.delete(testType.getIdentification().getId()));
storageAPI.createOrUpdate(testSubmodel);
assertTrue(storageAPI.delete(testSubmodel.getIdentification().getId()));
Collection<Submodel> allElements = storageAPI.retrieveAll();
assertFalse(allElements.contains(testType));
assertFalse(allElements.contains(testSubmodel));
}

@Test
public void proofPersistency() {
storageAPI.createOrUpdate(testSubmodel);
Submodel persistentSubmodel = getSecondStorageAPI().retrieve(testSubmodel.getIdentification().getId());
assertEquals(testSubmodel, persistentSubmodel);
}

/**
* This test must be implemented individually for every storage backend
*/
@Test
public abstract void createCollectionIfNotExists();

/**
* This test must be implemented individually for every storage backend
*/
@Test
public abstract void deleteCollection();

}
Loading

0 comments on commit eee2ec2

Please sign in to comment.