mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
CE-781 initial set of tets for mongodb
This commit is contained in:
@ -25,10 +25,12 @@ package com.kingsrook.qqq.backend.module.mongodb.actions;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
@ -86,7 +88,8 @@ public class AbstractMongoDBAction
|
|||||||
return (new MongoClientContainer(mongoDBTransaction.getMongoClient(), mongoDBTransaction.getClientSession(), false));
|
return (new MongoClientContainer(mongoDBTransaction.getMongoClient(), mongoDBTransaction.getClientSession(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionString connectionString = new ConnectionString("mongodb://" + backend.getHost() + ":" + backend.getPort() + "/");
|
String suffix = StringUtils.hasContent(backend.getUrlSuffix()) ? "?" + backend.getUrlSuffix() : "";
|
||||||
|
ConnectionString connectionString = new ConnectionString("mongodb://" + backend.getHost() + ":" + backend.getPort() + "/" + suffix);
|
||||||
|
|
||||||
MongoCredential credential = MongoCredential.createCredential(backend.getUsername(), backend.getAuthSourceDatabase(), backend.getPassword().toCharArray());
|
MongoCredential credential = MongoCredential.createCredential(backend.getUsername(), backend.getAuthSourceDatabase(), backend.getPassword().toCharArray());
|
||||||
|
|
||||||
@ -165,19 +168,66 @@ public class AbstractMongoDBAction
|
|||||||
QRecord record = new QRecord();
|
QRecord record = new QRecord();
|
||||||
record.setTableName(table.getName());
|
record.setTableName(table.getName());
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// todo - this - or iterate over the values in the document?? //
|
// first iterate over the table's fields, looking for them (at their backend name (path, //
|
||||||
// seems like, maybe, this is an attribute in the table-backend-details? //
|
// if it has dots) inside the document note that we'll remove values from the document //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
// as we go - then after this loop, will handle all remaining values as unstructured fields //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
Map<String, Serializable> values = record.getValues();
|
Map<String, Serializable> values = record.getValues();
|
||||||
for(QFieldMetaData field : table.getFields().values())
|
for(QFieldMetaData field : table.getFields().values())
|
||||||
{
|
{
|
||||||
String fieldBackendName = getFieldBackendName(field);
|
|
||||||
Object value = document.get(fieldBackendName);
|
|
||||||
String fieldName = field.getName();
|
String fieldName = field.getName();
|
||||||
|
String fieldBackendName = getFieldBackendName(field);
|
||||||
|
|
||||||
|
if(fieldBackendName.contains("."))
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// process backend-names with dots as hierarchical objects //
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
String[] parts = fieldBackendName.split("\\.");
|
||||||
|
Document tmpDocument = document;
|
||||||
|
for(int i = 0; i < parts.length - 1; i++)
|
||||||
|
{
|
||||||
|
if(!tmpDocument.containsKey(parts[i]))
|
||||||
|
{
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if we can't find the sub-document, break, and we won't have a value for this field (do we want null?) //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
setValue(values, fieldName, null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(tmpDocument.get(parts[i]) instanceof Document subDocument)
|
||||||
|
{
|
||||||
|
tmpDocument = subDocument;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.warn("Unexpected - In table [" + table.getName() + "] found a non-document at sub-key [" + parts[i] + "] for field [" + field.getName() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value = tmpDocument.remove(parts[parts.length - 1]);
|
||||||
setValue(values, fieldName, value);
|
setValue(values, fieldName, value);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Object value = document.remove(fieldBackendName);
|
||||||
|
setValue(values, fieldName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
// handle remaining values in the document as un-structured //
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
for(String subFieldName : document.keySet())
|
||||||
|
{
|
||||||
|
Object subValue = document.get(subFieldName);
|
||||||
|
setValue(values, subFieldName, subValue);
|
||||||
|
}
|
||||||
|
|
||||||
return (record);
|
return (record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,17 +277,23 @@ public class AbstractMongoDBAction
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Convert a QRecord to a mongodb document.
|
** Convert a QRecord to a mongodb document.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
protected Document recordToDocument(QTableMetaData table, QRecord record)
|
protected Document recordToDocument(QTableMetaData table, QRecord record) throws QException
|
||||||
{
|
{
|
||||||
Document document = new Document();
|
Document document = new Document();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// todo - this - or iterate over the values in the record?? //
|
// first iterate over fields defined in the table - put them in the document for mongo first. //
|
||||||
// seems like, maybe, this is an attribute in the table-backend-details? //
|
// track the names that we've processed in a set. then later we'll go over all values in the //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
// record and send them all to mongo (skipping ones we knew about from the table definition) //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Set<String> processedFields = new HashSet<>();
|
||||||
|
|
||||||
for(QFieldMetaData field : table.getFields().values())
|
for(QFieldMetaData field : table.getFields().values())
|
||||||
{
|
{
|
||||||
if(field.getName().equals(table.getPrimaryKeyField()) && record.getValue(field.getName()) == null)
|
Serializable value = record.getValue(field.getName());
|
||||||
|
processedFields.add(field.getName());
|
||||||
|
|
||||||
|
if(field.getName().equals(table.getPrimaryKeyField()) && value == null)
|
||||||
{
|
{
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
// let mongodb client generate id //
|
// let mongodb client generate id //
|
||||||
@ -246,8 +302,53 @@ public class AbstractMongoDBAction
|
|||||||
}
|
}
|
||||||
|
|
||||||
String fieldBackendName = getFieldBackendName(field);
|
String fieldBackendName = getFieldBackendName(field);
|
||||||
document.append(fieldBackendName, record.getValue(field.getName()));
|
if(fieldBackendName.contains("."))
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// process backend-names with dots as hierarchical objects //
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
String[] parts = fieldBackendName.split("\\.");
|
||||||
|
Document tmpDocument = document;
|
||||||
|
for(int i = 0; i < parts.length - 1; i++)
|
||||||
|
{
|
||||||
|
if(!tmpDocument.containsKey(parts[i]))
|
||||||
|
{
|
||||||
|
Document subDocument = new Document();
|
||||||
|
tmpDocument.put(parts[i], subDocument);
|
||||||
|
tmpDocument = subDocument;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(tmpDocument.get(parts[i]) instanceof Document subDocument)
|
||||||
|
{
|
||||||
|
tmpDocument = subDocument;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw (new QException("Fields in table [" + table.getName() + "] specify both a sub-object and a field at the key: " + parts[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpDocument.append(parts[parts.length - 1], value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
document.append(fieldBackendName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////
|
||||||
|
// do remaining values //
|
||||||
|
/////////////////////////
|
||||||
|
// for(Map.Entry<String, Serializable> entry : clone.getValues().entrySet())
|
||||||
|
for(Map.Entry<String, Serializable> entry : record.getValues().entrySet())
|
||||||
|
{
|
||||||
|
if(!processedFields.contains(entry.getKey()))
|
||||||
|
{
|
||||||
|
document.append(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (document);
|
return (document);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,126 +141,6 @@ public class MongoDBInsertAction extends AbstractMongoDBAction implements Insert
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (rs);
|
return (rs);
|
||||||
|
|
||||||
/*
|
|
||||||
try
|
|
||||||
{
|
|
||||||
List<QFieldMetaData> insertableFields = table.getFields().values().stream()
|
|
||||||
.filter(field -> !field.getName().equals("id")) // todo - intent here is to avoid non-insertable fields.
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
String columns = insertableFields.stream()
|
|
||||||
.map(f -> "`" + getColumnName(f) + "`")
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
String questionMarks = insertableFields.stream()
|
|
||||||
.map(x -> "?")
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
|
|
||||||
List<QRecord> outputRecords = new ArrayList<>();
|
|
||||||
rs.setRecords(outputRecords);
|
|
||||||
|
|
||||||
Connection connection;
|
|
||||||
boolean needToCloseConnection = false;
|
|
||||||
if(insertInput.getTransaction() != null && insertInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction)
|
|
||||||
{
|
|
||||||
connection = rdbmsTransaction.getConnection();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
connection = getConnection(insertInput);
|
|
||||||
needToCloseConnection = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(List<QRecord> page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE))
|
|
||||||
{
|
|
||||||
String tableName = escapeIdentifier(getTableName(table));
|
|
||||||
StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES");
|
|
||||||
List<Object> params = new ArrayList<>();
|
|
||||||
int recordIndex = 0;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// for each record in the page: //
|
|
||||||
// - if it has errors, skip it //
|
|
||||||
// - else add a "(?,?,...,?)," clause to the INSERT //
|
|
||||||
// - then add all fields into the params list //
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
for(QRecord record : page)
|
|
||||||
{
|
|
||||||
if(CollectionUtils.nullSafeHasContents(record.getErrors()))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(recordIndex++ > 0)
|
|
||||||
{
|
|
||||||
sql.append(",");
|
|
||||||
}
|
|
||||||
sql.append("(").append(questionMarks).append(")");
|
|
||||||
|
|
||||||
for(QFieldMetaData field : insertableFields)
|
|
||||||
{
|
|
||||||
Serializable value = record.getValue(field.getName());
|
|
||||||
value = scrubValue(field, value);
|
|
||||||
params.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// if all records had errors, copy them to the output, and continue w/o running query //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(recordIndex == 0)
|
|
||||||
{
|
|
||||||
for(QRecord record : page)
|
|
||||||
{
|
|
||||||
QRecord outputRecord = new QRecord(record);
|
|
||||||
outputRecords.add(outputRecord);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Long mark = System.currentTimeMillis();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
// execute the insert, then foreach record in the input, //
|
|
||||||
// add it to the output, and set its generated id too. //
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
// todo sql customization - can edit sql and/or param list
|
|
||||||
// todo - non-serial-id style tables
|
|
||||||
// todo - other generated values, e.g., createDate... maybe need to re-select?
|
|
||||||
List<Integer> idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params);
|
|
||||||
int index = 0;
|
|
||||||
for(QRecord record : page)
|
|
||||||
{
|
|
||||||
QRecord outputRecord = new QRecord(record);
|
|
||||||
outputRecords.add(outputRecord);
|
|
||||||
|
|
||||||
if(CollectionUtils.nullSafeIsEmpty(record.getErrors()))
|
|
||||||
{
|
|
||||||
Integer id = idList.get(index++);
|
|
||||||
outputRecord.setValue(table.getPrimaryKeyField(), id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logSQL(sql, params, mark);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if(needToCloseConnection)
|
|
||||||
{
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
throw new QException("Error executing insert: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,17 @@ import com.kingsrook.qqq.backend.core.context.QContext;
|
|||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.actions.AbstractMongoDBAction;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.actions.MongoClientContainer;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.model.metadata.MongoDBBackendMetaData;
|
||||||
|
import com.mongodb.client.MongoClient;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
|
import org.testcontainers.utility.DockerImageName;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -37,6 +46,27 @@ public class BaseTest
|
|||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(BaseTest.class);
|
private static final QLogger LOG = QLogger.getLogger(BaseTest.class);
|
||||||
|
|
||||||
|
private static GenericContainer<?> mongoDBContainer;
|
||||||
|
|
||||||
|
private static final String MONGO_IMAGE = "mongo:4.2.0-bionic";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll()
|
||||||
|
{
|
||||||
|
mongoDBContainer = new GenericContainer<>(DockerImageName.parse(MONGO_IMAGE))
|
||||||
|
.withEnv("MONGO_INITDB_ROOT_USERNAME", TestUtils.MONGO_USERNAME)
|
||||||
|
.withEnv("MONGO_INITDB_ROOT_PASSWORD", TestUtils.MONGO_PASSWORD)
|
||||||
|
.withEnv("MONGO_INITDB_DATABASE", TestUtils.MONGO_DATABASE)
|
||||||
|
.withExposedPorts(TestUtils.MONGO_PORT);
|
||||||
|
|
||||||
|
mongoDBContainer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -46,6 +76,13 @@ public class BaseTest
|
|||||||
void baseBeforeEach()
|
void baseBeforeEach()
|
||||||
{
|
{
|
||||||
QContext.init(TestUtils.defineInstance(), new QSession());
|
QContext.init(TestUtils.defineInstance(), new QSession());
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// host could(?) be different, and mapped port will be, so set them in backend meta-data based on our running container //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MongoDBBackendMetaData backend = (MongoDBBackendMetaData) QContext.getQInstance().getBackend(TestUtils.DEFAULT_BACKEND_NAME);
|
||||||
|
backend.setHost(mongoDBContainer.getHost());
|
||||||
|
backend.setPort(mongoDBContainer.getMappedPort(TestUtils.MONGO_PORT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -56,11 +93,43 @@ public class BaseTest
|
|||||||
@AfterEach
|
@AfterEach
|
||||||
void baseAfterEach()
|
void baseAfterEach()
|
||||||
{
|
{
|
||||||
|
///////////////////////////////////////
|
||||||
|
// clear test database between tests //
|
||||||
|
///////////////////////////////////////
|
||||||
|
MongoClient mongoClient = getMongoClient();
|
||||||
|
MongoDatabase database = mongoClient.getDatabase(TestUtils.MONGO_DATABASE);
|
||||||
|
database.drop();
|
||||||
|
|
||||||
QContext.clear();
|
QContext.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
protected static MongoClient getMongoClient()
|
||||||
|
{
|
||||||
|
MongoDBBackendMetaData backend = (MongoDBBackendMetaData) QContext.getQInstance().getBackend(TestUtils.DEFAULT_BACKEND_NAME);
|
||||||
|
MongoClientContainer mongoClientContainer = new AbstractMongoDBAction().openClient(backend, null);
|
||||||
|
MongoClient mongoClient = mongoClientContainer.getMongoClient();
|
||||||
|
return mongoClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@AfterAll
|
||||||
|
static void afterAll()
|
||||||
|
{
|
||||||
|
// this.mongoDbReplicaSet.close();
|
||||||
|
mongoDBContainer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** if needed, re-initialize the QInstance in context.
|
** if needed, re-initialize the QInstance in context.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -43,6 +43,13 @@ public class TestUtils
|
|||||||
|
|
||||||
public static final String SECURITY_KEY_STORE_ALL_ACCESS = "storeAllAccess";
|
public static final String SECURITY_KEY_STORE_ALL_ACCESS = "storeAllAccess";
|
||||||
|
|
||||||
|
public static final String MONGO_USERNAME = "mongoUser";
|
||||||
|
public static final String MONGO_PASSWORD = "password";
|
||||||
|
public static final Integer MONGO_PORT = 27017;
|
||||||
|
public static final String MONGO_DATABASE = "testDatabase";
|
||||||
|
|
||||||
|
public static final String TEST_COLLECTION = "testTable";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -105,12 +112,11 @@ public class TestUtils
|
|||||||
return (new MongoDBBackendMetaData()
|
return (new MongoDBBackendMetaData()
|
||||||
.withName(DEFAULT_BACKEND_NAME)
|
.withName(DEFAULT_BACKEND_NAME)
|
||||||
.withHost("localhost")
|
.withHost("localhost")
|
||||||
.withPort(27017)
|
.withPort(TestUtils.MONGO_PORT)
|
||||||
.withUsername("ctliveuser")
|
.withUsername(TestUtils.MONGO_USERNAME)
|
||||||
.withPassword("uoaKOIjfk23h8lozK983L")
|
.withPassword(TestUtils.MONGO_PASSWORD)
|
||||||
.withAuthSourceDatabase("admin")
|
.withAuthSourceDatabase("admin")
|
||||||
.withDatabaseName("testDatabase")
|
.withDatabaseName(TestUtils.MONGO_DATABASE));
|
||||||
/*.withUrlSuffix("?tls=true&tlsCAFile=global-bundle.pem&retryWrites=false")*/);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -128,8 +134,8 @@ public class TestUtils
|
|||||||
.withBackendName(DEFAULT_BACKEND_NAME)
|
.withBackendName(DEFAULT_BACKEND_NAME)
|
||||||
.withPrimaryKeyField("id")
|
.withPrimaryKeyField("id")
|
||||||
.withField(new QFieldMetaData("id", QFieldType.STRING).withBackendName("_id"))
|
.withField(new QFieldMetaData("id", QFieldType.STRING).withBackendName("_id"))
|
||||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME))
|
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withBackendName("metaData.createDate"))
|
||||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME))
|
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withBackendName("metaData.modifyDate"))
|
||||||
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
|
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
|
||||||
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
||||||
.withField(new QFieldMetaData("birthDate", QFieldType.DATE))
|
.withField(new QFieldMetaData("birthDate", QFieldType.DATE))
|
||||||
@ -139,7 +145,7 @@ public class TestUtils
|
|||||||
.withField(new QFieldMetaData("daysWorked", QFieldType.INTEGER))
|
.withField(new QFieldMetaData("daysWorked", QFieldType.INTEGER))
|
||||||
.withField(new QFieldMetaData("homeTown", QFieldType.STRING))
|
.withField(new QFieldMetaData("homeTown", QFieldType.STRING))
|
||||||
.withBackendDetails(new MongoDBTableBackendDetails()
|
.withBackendDetails(new MongoDBTableBackendDetails()
|
||||||
.withTableName("testTable"));
|
.withTableName(TEST_COLLECTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.module.mongodb.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.AggregateAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOperator;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByGroupBy;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for MongoDBQueryAction
|
||||||
|
*******************************************************************************/
|
||||||
|
class MongoDBAggregateActionTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
InsertInput insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
insertInput.setRecords(List.of(
|
||||||
|
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff").withValue("isEmployed", true).withValue("annualSalary", 1),
|
||||||
|
new QRecord().withValue("firstName", "Linda").withValue("lastName", "Kelkhoff").withValue("isEmployed", true).withValue("annualSalary", 5),
|
||||||
|
new QRecord().withValue("firstName", "Tim").withValue("lastName", "Chamberlain").withValue("isEmployed", true).withValue("annualSalary", 3),
|
||||||
|
new QRecord().withValue("firstName", "James").withValue("lastName", "Maes").withValue("isEmployed", true).withValue("annualSalary", 5),
|
||||||
|
new QRecord().withValue("firstName", "J.D.").withValue("lastName", "Maes").withValue("isEmployed", false).withValue("annualSalary", 0)
|
||||||
|
));
|
||||||
|
new InsertAction().execute(insertInput);
|
||||||
|
|
||||||
|
{
|
||||||
|
AggregateInput aggregateInput = new AggregateInput();
|
||||||
|
aggregateInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
aggregateInput.setFilter(new QQueryFilter()
|
||||||
|
.withOrderBy(new QFilterOrderByAggregate(new Aggregate("annualSalary", AggregateOperator.MAX)).withIsAscending(false))
|
||||||
|
.withOrderBy(new QFilterOrderByGroupBy(new GroupBy(QFieldType.STRING, "lastName")))
|
||||||
|
);
|
||||||
|
aggregateInput.withAggregate(new Aggregate("id", AggregateOperator.COUNT));
|
||||||
|
aggregateInput.withAggregate(new Aggregate("annualSalary", AggregateOperator.SUM));
|
||||||
|
aggregateInput.withAggregate(new Aggregate("annualSalary", AggregateOperator.MAX));
|
||||||
|
aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName"));
|
||||||
|
aggregateInput.withGroupBy(new GroupBy(QFieldType.BOOLEAN, "isEmployed"));
|
||||||
|
AggregateOutput aggregateOutput = new AggregateAction().execute(aggregateInput);
|
||||||
|
// todo - actual assertions
|
||||||
|
}
|
||||||
|
{
|
||||||
|
AggregateInput aggregateInput = new AggregateInput();
|
||||||
|
aggregateInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
aggregateInput.withAggregate(new Aggregate("id", AggregateOperator.COUNT));
|
||||||
|
aggregateInput.withAggregate(new Aggregate("annualSalary", AggregateOperator.AVG));
|
||||||
|
AggregateOutput aggregateOutput = new AggregateAction().execute(aggregateInput);
|
||||||
|
// todo - actual assertions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.module.mongodb.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.TestUtils;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for MongoDBQueryAction
|
||||||
|
*******************************************************************************/
|
||||||
|
class MongoDBCountActionTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
////////////////////////////////////////
|
||||||
|
// directly insert some mongo records //
|
||||||
|
////////////////////////////////////////
|
||||||
|
MongoDatabase database = getMongoClient().getDatabase(TestUtils.MONGO_DATABASE);
|
||||||
|
MongoCollection<Document> collection = database.getCollection(TestUtils.TEST_COLLECTION);
|
||||||
|
collection.insertMany(List.of(
|
||||||
|
Document.parse("""
|
||||||
|
{"firstName": "Darin", "lastName": "Kelkhoff"}"""),
|
||||||
|
Document.parse("""
|
||||||
|
{"firstName": "Tylers", "lastName": "Sample"}"""),
|
||||||
|
Document.parse("""
|
||||||
|
{"firstName": "Tylers", "lastName": "Simple"}"""),
|
||||||
|
Document.parse("""
|
||||||
|
{"firstName": "Thom", "lastName": "Chutterloin"}""")
|
||||||
|
));
|
||||||
|
|
||||||
|
CountInput countInput = new CountInput();
|
||||||
|
countInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
assertEquals(4, new CountAction().execute(countInput).getCount());
|
||||||
|
|
||||||
|
countInput.setFilter(new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, "Tylers")));
|
||||||
|
assertEquals(2, new CountAction().execute(countInput).getCount());
|
||||||
|
|
||||||
|
countInput.setFilter(new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, "assdf")));
|
||||||
|
assertEquals(0, new CountAction().execute(countInput).getCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.module.mongodb.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.TestUtils;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for MongoDBQueryAction
|
||||||
|
*******************************************************************************/
|
||||||
|
class MongoDBDeleteActionTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
////////////////////////////////////////
|
||||||
|
// directly insert some mongo records //
|
||||||
|
////////////////////////////////////////
|
||||||
|
MongoDatabase database = getMongoClient().getDatabase(TestUtils.MONGO_DATABASE);
|
||||||
|
MongoCollection<Document> collection = database.getCollection(TestUtils.TEST_COLLECTION);
|
||||||
|
collection.insertMany(List.of(
|
||||||
|
Document.parse("""
|
||||||
|
{"firstName": "Darin", "lastName": "Kelkhoff"}"""),
|
||||||
|
Document.parse("""
|
||||||
|
{"firstName": "Tylers", "lastName": "Sample"}"""),
|
||||||
|
Document.parse("""
|
||||||
|
{"firstName": "Tylers", "lastName": "Simple"}"""),
|
||||||
|
Document.parse("""
|
||||||
|
{"firstName": "Thom", "lastName": "Chutterloin"}""")
|
||||||
|
));
|
||||||
|
assertEquals(4, collection.countDocuments());
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// do a delete by id (look it up first) //
|
||||||
|
//////////////////////////////////////////
|
||||||
|
{
|
||||||
|
QueryInput queryInput = new QueryInput();
|
||||||
|
queryInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, "Darin")));
|
||||||
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
|
String id0 = queryOutput.getRecords().get(0).getValueString("id");
|
||||||
|
|
||||||
|
DeleteInput deleteInput = new DeleteInput();
|
||||||
|
deleteInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
deleteInput.setPrimaryKeys(List.of(id0));
|
||||||
|
assertEquals(1, new DeleteAction().execute(deleteInput).getDeletedRecordCount());
|
||||||
|
}
|
||||||
|
assertEquals(3, collection.countDocuments());
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// do a delete by filter //
|
||||||
|
///////////////////////////
|
||||||
|
{
|
||||||
|
DeleteInput deleteInput = new DeleteInput();
|
||||||
|
deleteInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
deleteInput.setQueryFilter(new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, "Tylers")));
|
||||||
|
assertEquals(2, new DeleteAction().execute(deleteInput).getDeletedRecordCount());
|
||||||
|
}
|
||||||
|
assertEquals(1, collection.countDocuments());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.module.mongodb.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.TestUtils;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for MongoDBQueryAction
|
||||||
|
*******************************************************************************/
|
||||||
|
class MongoDBInsertActionTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
InsertInput insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
insertInput.setRecords(List.of(
|
||||||
|
new QRecord().withValue("firstName", "Darin")
|
||||||
|
.withValue("unmappedField", 1701)
|
||||||
|
.withValue("unmappedList", new ArrayList<>(List.of("A", "B", "C")))
|
||||||
|
.withValue("unmappedObject", new HashMap<>(Map.of("A", 1, "C", true))),
|
||||||
|
new QRecord().withValue("firstName", "Tim"),
|
||||||
|
new QRecord().withValue("firstName", "Tyler")
|
||||||
|
));
|
||||||
|
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// make sure id got put on all records //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
for(QRecord record : insertOutput.getRecords())
|
||||||
|
{
|
||||||
|
assertNotNull(record.getValueString("id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// directly query mongo for the inserted records //
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
MongoDatabase database = getMongoClient().getDatabase(TestUtils.MONGO_DATABASE);
|
||||||
|
MongoCollection<Document> collection = database.getCollection(TestUtils.TEST_COLLECTION);
|
||||||
|
assertEquals(3, collection.countDocuments());
|
||||||
|
for(Document document : collection.find())
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// make sure values got set - including some nested values //
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
assertNotNull(document.get("firstName"));
|
||||||
|
assertNotNull(document.get("metaData"));
|
||||||
|
assertThat(document.get("metaData")).isInstanceOf(Document.class);
|
||||||
|
assertNotNull(((Document) document.get("metaData")).get("createDate"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Document document = collection.find(new Document("firstName", "Darin")).first();
|
||||||
|
assertNotNull(document);
|
||||||
|
assertEquals(1701, document.get("unmappedField"));
|
||||||
|
assertEquals(List.of("A", "B", "C"), document.get("unmappedList"));
|
||||||
|
assertEquals(Map.of("A", 1, "C", true), document.get("unmappedObject"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.module.mongodb.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.module.mongodb.TestUtils;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for MongoDBQueryAction
|
||||||
|
*******************************************************************************/
|
||||||
|
class MongoDBQueryActionTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
////////////////////////////////////////
|
||||||
|
// directly insert some mongo records //
|
||||||
|
////////////////////////////////////////
|
||||||
|
MongoDatabase database = getMongoClient().getDatabase(TestUtils.MONGO_DATABASE);
|
||||||
|
MongoCollection<Document> collection = database.getCollection(TestUtils.TEST_COLLECTION);
|
||||||
|
collection.insertMany(List.of(
|
||||||
|
Document.parse("""
|
||||||
|
{ "metaData": {"createDate": "2023-01-09T01:01:01.123Z", "modifyDate": "2023-01-09T02:02:02.123Z", "oops": "All Crunchberries"},
|
||||||
|
"firstName": "Darin",
|
||||||
|
"lastName": "Kelkhoff",
|
||||||
|
"unmappedField": 1701,
|
||||||
|
"unmappedList": [1,2,3],
|
||||||
|
"unmappedObject": {
|
||||||
|
"A": "B",
|
||||||
|
"One": 2,
|
||||||
|
"subSub": {
|
||||||
|
"so": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"""),
|
||||||
|
Document.parse("""
|
||||||
|
{"metaData": {"createDate": "2023-01-09T03:03:03.123Z", "modifyDate": "2023-01-09T04:04:04.123Z"}, "firstName": "Tylers", "lastName": "Sample"}""")
|
||||||
|
));
|
||||||
|
|
||||||
|
QueryInput queryInput = new QueryInput();
|
||||||
|
queryInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
|
|
||||||
|
assertEquals(2, queryOutput.getRecords().size());
|
||||||
|
|
||||||
|
QRecord record = queryOutput.getRecords().get(0);
|
||||||
|
assertEquals(Instant.parse("2023-01-09T01:01:01.123Z"), record.getValueInstant("createDate"));
|
||||||
|
assertEquals(Instant.parse("2023-01-09T02:02:02.123Z"), record.getValueInstant("modifyDate"));
|
||||||
|
assertThat(record.getValue("id")).isInstanceOf(String.class);
|
||||||
|
assertEquals("Darin", record.getValueString("firstName"));
|
||||||
|
assertEquals("Kelkhoff", record.getValueString("lastName"));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// test that un-mapped (or un-structured) fields come through, with their shape as they exist in the mongo record //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
assertEquals(1701, record.getValueInteger("unmappedField"));
|
||||||
|
assertEquals(List.of(1, 2, 3), record.getValue("unmappedList"));
|
||||||
|
assertEquals(Map.of("A", "B", "One", 2, "subSub", Map.of("so", true)), record.getValue("unmappedObject"));
|
||||||
|
assertEquals(Map.of("oops", "All Crunchberries"), record.getValue("metaData"));
|
||||||
|
|
||||||
|
record = queryOutput.getRecords().get(1);
|
||||||
|
assertEquals(Instant.parse("2023-01-09T03:03:03.123Z"), record.getValueInstant("createDate"));
|
||||||
|
assertEquals(Instant.parse("2023-01-09T04:04:04.123Z"), record.getValueInstant("modifyDate"));
|
||||||
|
assertEquals("Tylers", record.getValueString("firstName"));
|
||||||
|
assertEquals("Sample", record.getValueString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -19,26 +19,27 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.kingsrook.qqq.backend.core.actions.interfaces;
|
package com.kingsrook.qqq.backend.module.mongodb.actions;
|
||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.module.mongodb.BaseTest;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Unit test for MongoDBUpdateAction
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public interface QActionInterface
|
class MongoDBUpdateActionTest extends BaseTest
|
||||||
{
|
{
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
default QBackendTransaction openTransaction(AbstractTableActionInput input) throws QException
|
@Test
|
||||||
|
void test() throws QException
|
||||||
{
|
{
|
||||||
return (new QBackendTransaction());
|
// todo - test!!
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user