Skip to content

Commit

Permalink
Implements custom mongodb mapping converter (#81)
Browse files Browse the repository at this point in the history
Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>
  • Loading branch information
mdanish98 authored Sep 8, 2023
1 parent 6b60bfe commit 8674a1e
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 41 deletions.
14 changes: 13 additions & 1 deletion basyx.aasrepository/basyx.aasrepository-backend-mongodb/pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
Expand Down Expand Up @@ -46,5 +47,16 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.http</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2021 the Eclipse BaSyx Authors
* Copyright (C) 2023 the Eclipse BaSyx Authors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
Expand Down Expand Up @@ -27,30 +27,54 @@

import static org.junit.Assert.assertEquals;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.bson.Document;
import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetAdministrationShell;
import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory;
import org.eclipse.digitaltwin.basyx.common.mongocore.CustomIdentifiableMappingMongoConverter;
import org.eclipse.digitaltwin.basyx.common.mongocore.MongoDBUtilities;
import org.eclipse.digitaltwin.basyx.http.Aas4JHTTPSerializationExtension;
import org.eclipse.digitaltwin.basyx.http.BaSyxHTTPConfiguration;
import org.eclipse.digitaltwin.basyx.http.SerializationExtension;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;

/**
* Tests the {@link MongoDBAasRepository}
*
* @author schnicke, danish, kammognie
*
*/
public class TestMongoDBAasRepository extends AasRepositorySuite {
private final String COLLECTION = "aasTestCollection";


private static final String CONFIGURED_AAS_REPO_NAME = "configured-aas-repo-name";

private final String COLLECTION = "aasTestCollection";

private MongoTemplate template;

@Override
protected AasRepositoryFactory getAasRepositoryFactory() {
MongoTemplate template = createMongoTemplate();
template = createMongoTemplate();

MongoDBUtilities.clearCollection(template, COLLECTION);

Expand All @@ -60,22 +84,21 @@ protected AasRepositoryFactory getAasRepositoryFactory() {
@Test
public void aasIsPersisted() {
AasRepositoryFactory repoFactory = getAasRepositoryFactory();
AssetAdministrationShell expectedShell = createDummyShellOnRepo(repoFactory.create());
AssetAdministrationShell expectedShell = createDummyShellOnRepo(repoFactory.create(), "dummy");
AssetAdministrationShell retrievedShell = getAasFromNewBackendInstance(repoFactory, expectedShell.getId());

assertEquals(expectedShell, retrievedShell);

}

@Test
public void updatedAasIsPersisted() {
AasRepositoryFactory repoFactory = getAasRepositoryFactory();
AasRepository mongoDBAasRepository = repoFactory.create();
AssetAdministrationShell expectedShell = createDummyShellOnRepo(mongoDBAasRepository);
AssetAdministrationShell expectedShell = createDummyShellOnRepo(mongoDBAasRepository, "dummy");
addSubmodelReferenceToAas(expectedShell);
mongoDBAasRepository.updateAas(expectedShell.getId(), expectedShell);
AssetAdministrationShell retrievedShell = getAasFromNewBackendInstance(repoFactory, expectedShell.getId());

assertEquals(expectedShell, retrievedShell);
}

Expand All @@ -85,6 +108,26 @@ public void getConfiguredMongoDBAasRepositoryName() {

assertEquals(CONFIGURED_AAS_REPO_NAME, repo.getName());
}

@Test
public void retrieveRawAasJson() throws FileNotFoundException, IOException {
AssetAdministrationShell dummyAas = createDummyShellOnRepo(getAasRepositoryFactory().create(), "dummyAAS");

String expectedAASJson = getAasJSONString();

template.save(dummyAas, COLLECTION);

Document aasDocument = template.findOne(new Query().addCriteria(Criteria.where("id").is("dummyAAS")),
Document.class, COLLECTION);

assertSameJSONContent(expectedAASJson, aasDocument.toJson());
}

private void assertSameJSONContent(String expectedAASJson, String json) throws JsonMappingException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();

assertEquals(mapper.readTree(expectedAASJson), mapper.readTree(json));
}

private void addSubmodelReferenceToAas(AssetAdministrationShell expectedShell) {
expectedShell.setSubmodels(Arrays.asList(AasRepositorySuite.createDummyReference("dummySubmodel")));
Expand All @@ -96,20 +139,41 @@ private AssetAdministrationShell getAasFromNewBackendInstance(AasRepositoryFacto
return retrievedShell;
}

private AssetAdministrationShell createDummyShellOnRepo(AasRepository aasRepository) {
AssetAdministrationShell expectedShell = new DefaultAssetAdministrationShell.Builder().id("dummy")
private AssetAdministrationShell createDummyShellOnRepo(AasRepository aasRepository, String id) {
AssetAdministrationShell expectedShell = new DefaultAssetAdministrationShell.Builder().id(id).idShort("dummyAASIdShort")
.build();

aasRepository.createAas(expectedShell);
return expectedShell;
}

private MongoTemplate createMongoTemplate() {
String connectionURL = "mongodb://mongoAdmin:mongoPassword@localhost:27017/";
List<SerializationExtension> extensions = Arrays.asList(new Aas4JHTTPSerializationExtension());

ObjectMapper mapper = new BaSyxHTTPConfiguration().jackson2ObjectMapperBuilder(extensions).build();

MongoClient client = MongoClients.create(connectionURL);
MongoDatabaseFactory databaseFactory = createDatabaseFactory();

return new MongoTemplate(databaseFactory, new CustomIdentifiableMappingMongoConverter(databaseFactory, new MongoMappingContext(), mapper));
}

private MongoDatabaseFactory createDatabaseFactory() {
String connectionString = createConnectionString();

MongoClient client = MongoClients.create(connectionString);

return new SimpleMongoClientDatabaseFactory(client, "test");
}

private String createConnectionString() {
return String.format("mongodb://%s:%s@%s:%s", "mongoAdmin", "mongoPassword", "127.0.0.1", "27017");
}

private String getAasJSONString() throws FileNotFoundException, IOException {
ClassPathResource classPathResource = new ClassPathResource("DummyAas.json");
InputStream in = classPathResource.getInputStream();

return new MongoTemplate(client, "BaSyxTestDb");
return IOUtils.toString(in, StandardCharsets.UTF_8.name());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"modelType": "AssetAdministrationShell",
"assetInformation": null,
"id": "dummyAAS",
"idShort": "dummyAASIdShort"
}
4 changes: 4 additions & 0 deletions basyx.common/basyx.mongocore/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,9 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.digitaltwin.aas4j</groupId>
<artifactId>dataformat-json</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*******************************************************************************
* Copyright (C) 2023 the Eclipse BaSyx Authors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
******************************************************************************/

package org.eclipse.digitaltwin.basyx.common.mongocore;

import java.io.IOException;

import org.bson.Document;
import org.bson.conversions.Bson;
import org.eclipse.digitaltwin.aas4j.v3.model.Identifiable;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
* Custom abstract mongodb mapping converter for applying mixins to the
* metamodels for conformance to the specification.
*
* @author danish
*
*/
@Component
@ConditionalOnProperty(prefix = "basyx", name = "backend", havingValue = "MongoDB")
public class CustomIdentifiableMappingMongoConverter extends MappingMongoConverter {

private static final String MONGODB_ID_KEY = "_id";

private ObjectMapper mapper;

public CustomIdentifiableMappingMongoConverter(MongoDatabaseFactory databaseFactory, MongoMappingContext mappingContext,
ObjectMapper mapper) {
super(new DefaultDbRefResolver(databaseFactory), mappingContext);
this.mapper = mapper;
}

@Override
public <S> S read(Class<S> clazz, Bson source) {
Document doc = (Document) source;

doc.remove(MONGODB_ID_KEY);

String metamodelJson = doc.toJson();

return deserializeMetamodel(clazz, metamodelJson);
}

@Override
public void write(Object source, Bson target) {
Document document = (Document) target;

String id = ((Identifiable) source).getId();

String json = serializeMetamodel(source);

document.put(MONGODB_ID_KEY, id);
document.putAll(Document.parse(json));
}

private String serializeMetamodel(Object source) {
try {
return mapper.writeValueAsString(source);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}

private <S> S deserializeMetamodel(Class<S> clazz, String string) {
try {
return mapper.readValue(string, clazz);
} catch (IOException e) {
throw new RuntimeException(string, e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,21 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.conceptdescriptionrepository-http</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.mongodbcore</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Loading

0 comments on commit 8674a1e

Please sign in to comment.