mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Add Memory backend module
This commit is contained in:
@ -72,6 +72,7 @@ public class QBackendModuleDispatcher
|
||||
// todo - let modules somehow "export" their types here?
|
||||
// e.g., backend-core shouldn't need to "know" about the modules.
|
||||
"com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockBackendModule",
|
||||
"com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.filesystem.local.FilesystemBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.filesystem.s3.S3BackendModule"
|
||||
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.UpdateInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A simple (probably only valid for testing?) implementation of the QModuleInterface,
|
||||
** that just stores its records in-memory.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MemoryBackendModule implements QBackendModuleInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Method where a backend module must be able to provide its type (name).
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getBackendType()
|
||||
{
|
||||
return ("memory");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for backend meta data for this module.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<? extends QBackendMetaData> getBackendMetaDataClass()
|
||||
{
|
||||
return (QBackendMetaData.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public CountInterface getCountInterface()
|
||||
{
|
||||
return new MemoryCountAction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QueryInterface getQueryInterface()
|
||||
{
|
||||
return new MemoryQueryAction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public InsertInterface getInsertInterface()
|
||||
{
|
||||
return (new MemoryInsertAction());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public UpdateInterface getUpdateInterface()
|
||||
{
|
||||
return (new MemoryUpdateAction());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public DeleteInterface getDeleteInterface()
|
||||
{
|
||||
return (new MemoryDeleteAction());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
|
||||
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.count.CountOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** In-memory version of count action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MemoryCountAction implements CountInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public CountOutput execute(CountInput countInput) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
CountOutput countOutput = new CountOutput();
|
||||
countOutput.setCount(MemoryRecordStore.getInstance().count(countInput));
|
||||
return (countOutput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new QException("Error executing count", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
|
||||
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.delete.DeleteOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** In-memory version of delete action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MemoryDeleteAction implements DeleteInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public DeleteOutput execute(DeleteInput deleteInput) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteOutput deleteOutput = new DeleteOutput();
|
||||
deleteOutput.setDeletedRecordCount(MemoryRecordStore.getInstance().delete(deleteInput));
|
||||
return (deleteOutput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new QException("Error executing delete: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface;
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** In-memory version of insert action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MemoryInsertAction implements InsertInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public InsertOutput execute(InsertInput insertInput) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
InsertOutput insertOutput = new InsertOutput();
|
||||
insertOutput.setRecords(MemoryRecordStore.getInstance().insert(insertInput, true));
|
||||
return (insertOutput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new QException("Error executing insert: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** In-memory version of query action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MemoryQueryAction implements QueryInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryOutput execute(QueryInput queryInput) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
QueryOutput queryOutput = new QueryOutput(queryInput);
|
||||
queryOutput.addRecords(MemoryRecordStore.getInstance().query(queryInput));
|
||||
return (queryOutput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new QException("Error executing query", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Storage provider for the MemoryBackendModule
|
||||
*******************************************************************************/
|
||||
public class MemoryRecordStore
|
||||
{
|
||||
private static MemoryRecordStore instance;
|
||||
|
||||
private Map<String, Map<Serializable, QRecord>> data;
|
||||
private Map<String, Integer> nextSerials;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** private singleton constructor
|
||||
*******************************************************************************/
|
||||
private MemoryRecordStore()
|
||||
{
|
||||
data = new HashMap<>();
|
||||
nextSerials = new HashMap<>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Forget all data in the memory store...
|
||||
*******************************************************************************/
|
||||
public void reset()
|
||||
{
|
||||
data.clear();
|
||||
nextSerials.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** singleton accessor
|
||||
*******************************************************************************/
|
||||
public static MemoryRecordStore getInstance()
|
||||
{
|
||||
if(instance == null)
|
||||
{
|
||||
instance = new MemoryRecordStore();
|
||||
}
|
||||
return (instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Map<Serializable, QRecord> getTableData(QTableMetaData table)
|
||||
{
|
||||
if(!data.containsKey(table.getName()))
|
||||
{
|
||||
data.put(table.getName(), new HashMap<>());
|
||||
}
|
||||
return (data.get(table.getName()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QRecord> query(QueryInput input)
|
||||
{
|
||||
Map<Serializable, QRecord> tableData = getTableData(input.getTable());
|
||||
List<QRecord> records = new ArrayList<>(tableData.values());
|
||||
// todo - filtering
|
||||
return (records);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer count(CountInput input)
|
||||
{
|
||||
Map<Serializable, QRecord> tableData = getTableData(input.getTable());
|
||||
List<QRecord> records = new ArrayList<>(tableData.values());
|
||||
// todo - filtering
|
||||
return (records.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QRecord> insert(InsertInput input, boolean returnInsertedRecords)
|
||||
{
|
||||
if(input.getRecords() == null)
|
||||
{
|
||||
return (new ArrayList<>());
|
||||
}
|
||||
|
||||
QTableMetaData table = input.getTable();
|
||||
Map<Serializable, QRecord> tableData = getTableData(table);
|
||||
Integer nextSerial = nextSerials.get(table.getName());
|
||||
if(nextSerial == null)
|
||||
{
|
||||
nextSerial = 1;
|
||||
while(tableData.containsKey(nextSerial))
|
||||
{
|
||||
nextSerial++;
|
||||
}
|
||||
}
|
||||
|
||||
List<QRecord> outputRecords = new ArrayList<>();
|
||||
QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField());
|
||||
for(QRecord record : input.getRecords())
|
||||
{
|
||||
if(record.getValue(primaryKeyField.getName()) == null && primaryKeyField.getType().equals(QFieldType.INTEGER))
|
||||
{
|
||||
record.setValue(primaryKeyField.getName(), nextSerial++);
|
||||
}
|
||||
|
||||
tableData.put(record.getValue(primaryKeyField.getName()), record);
|
||||
if(returnInsertedRecords)
|
||||
{
|
||||
outputRecords.add(record);
|
||||
}
|
||||
}
|
||||
|
||||
return (outputRecords);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QRecord> update(UpdateInput input, boolean returnUpdatedRecords)
|
||||
{
|
||||
if(input.getRecords() == null)
|
||||
{
|
||||
return (new ArrayList<>());
|
||||
}
|
||||
|
||||
QTableMetaData table = input.getTable();
|
||||
Map<Serializable, QRecord> tableData = getTableData(table);
|
||||
|
||||
List<QRecord> outputRecords = new ArrayList<>();
|
||||
QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField());
|
||||
for(QRecord record : input.getRecords())
|
||||
{
|
||||
Serializable primaryKeyValue = record.getValue(primaryKeyField.getName());
|
||||
if(tableData.containsKey(primaryKeyValue))
|
||||
{
|
||||
QRecord recordToUpdate = tableData.get(primaryKeyValue);
|
||||
for(Map.Entry<String, Serializable> valueEntry : record.getValues().entrySet())
|
||||
{
|
||||
recordToUpdate.setValue(valueEntry.getKey(), valueEntry.getValue());
|
||||
}
|
||||
|
||||
if(returnUpdatedRecords)
|
||||
{
|
||||
outputRecords.add(record);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outputRecords.add(record);
|
||||
}
|
||||
}
|
||||
|
||||
return (outputRecords);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public int delete(DeleteInput input)
|
||||
{
|
||||
if(input.getPrimaryKeys() == null)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
QTableMetaData table = input.getTable();
|
||||
Map<Serializable, QRecord> tableData = getTableData(table);
|
||||
int rowsDeleted = 0;
|
||||
for(Serializable primaryKeyValue : input.getPrimaryKeys())
|
||||
{
|
||||
if(tableData.containsKey(primaryKeyValue))
|
||||
{
|
||||
tableData.remove(primaryKeyValue);
|
||||
rowsDeleted++;
|
||||
}
|
||||
}
|
||||
|
||||
return (rowsDeleted);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.UpdateInterface;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** In-memory version of update action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MemoryUpdateAction implements UpdateInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public UpdateOutput execute(UpdateInput updateInput) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateOutput updateOutput = new UpdateOutput();
|
||||
updateOutput.setRecords(MemoryRecordStore.getInstance().update(updateInput, true));
|
||||
return (updateOutput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new QException("Error executing update: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
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.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
||||
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.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for MemoryBackendModule
|
||||
*******************************************************************************/
|
||||
class MemoryBackendModuleTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@AfterEach
|
||||
void afterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testFullCRUD() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_SHAPE);
|
||||
QSession session = new QSession();
|
||||
|
||||
/////////////////////////
|
||||
// do an initial count //
|
||||
/////////////////////////
|
||||
CountInput countInput = new CountInput(qInstance);
|
||||
countInput.setSession(session);
|
||||
countInput.setTableName(table.getName());
|
||||
assertEquals(0, new CountAction().execute(countInput).getCount());
|
||||
|
||||
//////////////////
|
||||
// do an insert //
|
||||
//////////////////
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(session);
|
||||
insertInput.setTableName(table.getName());
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord()
|
||||
.withTableName(table.getName())
|
||||
.withValue("name", "My Triangle")
|
||||
.withValue("type", "triangle")
|
||||
.withValue("noOfSides", 3)
|
||||
.withValue("isPolygon", true),
|
||||
new QRecord()
|
||||
.withTableName(table.getName())
|
||||
.withValue("name", "Your Square")
|
||||
.withValue("type", "square")
|
||||
.withValue("noOfSides", 4)
|
||||
.withValue("isPolygon", true),
|
||||
new QRecord()
|
||||
.withTableName(table.getName())
|
||||
.withValue("name", "Some Circle")
|
||||
.withValue("type", "circle")
|
||||
.withValue("noOfSides", null)
|
||||
.withValue("isPolygon", false)
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals(insertOutput.getRecords().size(), 3);
|
||||
assertTrue(insertOutput.getRecords().stream().allMatch(r -> r.getValue("id") != null));
|
||||
assertTrue(insertOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(1)));
|
||||
assertTrue(insertOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2)));
|
||||
assertTrue(insertOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(3)));
|
||||
|
||||
////////////////
|
||||
// do a query //
|
||||
////////////////
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setSession(session);
|
||||
queryInput.setTableName(table.getName());
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(queryOutput.getRecords().size(), 3);
|
||||
assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValue("id") != null));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(1)));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2)));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(3)));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueString("name").equals("My Triangle")));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueString("name").equals("Your Square")));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueString("name").equals("Some Circle")));
|
||||
|
||||
assertEquals(3, new CountAction().execute(countInput).getCount());
|
||||
|
||||
//////////////////
|
||||
// do an update //
|
||||
//////////////////
|
||||
UpdateInput updateInput = new UpdateInput(qInstance);
|
||||
updateInput.setSession(session);
|
||||
updateInput.setTableName(table.getName());
|
||||
updateInput.setRecords(List.of(
|
||||
new QRecord()
|
||||
.withTableName(table.getName())
|
||||
.withValue("id", 1)
|
||||
.withValue("name", "Not My Triangle any more"),
|
||||
new QRecord()
|
||||
.withTableName(table.getName())
|
||||
.withValue("id", 3)
|
||||
.withValue("type", "ellipse")
|
||||
));
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
assertEquals(updateOutput.getRecords().size(), 2);
|
||||
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(queryOutput.getRecords().size(), 3);
|
||||
assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("name").equals("My Triangle")));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueString("name").equals("Not My Triangle any more")));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueString("type").equals("ellipse")));
|
||||
assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("type").equals("circle")));
|
||||
|
||||
assertEquals(3, new CountAction().execute(countInput).getCount());
|
||||
|
||||
/////////////////
|
||||
// do a delete //
|
||||
/////////////////
|
||||
DeleteInput deleteInput = new DeleteInput(qInstance);
|
||||
deleteInput.setSession(session);
|
||||
deleteInput.setTableName(table.getName());
|
||||
deleteInput.setPrimaryKeys(List.of(1, 2));
|
||||
DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput);
|
||||
assertEquals(deleteOutput.getDeletedRecordCount(), 2);
|
||||
|
||||
assertEquals(1, new CountAction().execute(countInput).getCount());
|
||||
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(queryOutput.getRecords().size(), 1);
|
||||
assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueInteger("id").equals(1)));
|
||||
assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueInteger("id").equals(2)));
|
||||
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(3)));
|
||||
}
|
||||
|
||||
}
|
@ -26,7 +26,6 @@ import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.GetAgeStatistics;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
|
||||
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;
|
||||
@ -52,6 +51,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.MockAuthenticationModule;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockBackendModule;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicETLProcess;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamed.StreamedETLProcess;
|
||||
@ -65,12 +65,14 @@ import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackend
|
||||
public class TestUtils
|
||||
{
|
||||
public static final String DEFAULT_BACKEND_NAME = "default";
|
||||
public static final String MEMORY_BACKEND_NAME = "memory";
|
||||
|
||||
public static final String APP_NAME_GREETINGS = "greetingsApp";
|
||||
public static final String APP_NAME_PEOPLE = "peopleApp";
|
||||
public static final String APP_NAME_MISCELLANEOUS = "miscellaneous";
|
||||
|
||||
public static final String TABLE_NAME_PERSON = "person";
|
||||
public static final String TABLE_NAME_SHAPE = "shape";
|
||||
|
||||
public static final String PROCESS_NAME_GREET_PEOPLE = "greet";
|
||||
public static final String PROCESS_NAME_GREET_PEOPLE_INTERACTIVE = "greetInteractive";
|
||||
@ -89,10 +91,12 @@ public class TestUtils
|
||||
QInstance qInstance = new QInstance();
|
||||
qInstance.setAuthentication(defineAuthentication());
|
||||
qInstance.addBackend(defineBackend());
|
||||
qInstance.addBackend(defineMemoryBackend());
|
||||
|
||||
qInstance.addTable(defineTablePerson());
|
||||
qInstance.addTable(definePersonFileTable());
|
||||
qInstance.addTable(defineTableIdAndNameOnly());
|
||||
qInstance.addTable(defineTableShape());
|
||||
|
||||
qInstance.addPossibleValueSource(defineStatesPossibleValueSource());
|
||||
|
||||
@ -104,8 +108,6 @@ public class TestUtils
|
||||
|
||||
defineApps(qInstance);
|
||||
|
||||
System.out.println(new QInstanceAdapter().qInstanceToJson(qInstance));
|
||||
|
||||
return (qInstance);
|
||||
}
|
||||
|
||||
@ -174,6 +176,18 @@ public class TestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define the in-memory backend used in standard tests
|
||||
*******************************************************************************/
|
||||
public static QBackendMetaData defineMemoryBackend()
|
||||
{
|
||||
return new QBackendMetaData()
|
||||
.withName(MEMORY_BACKEND_NAME)
|
||||
.withBackendType(MemoryBackendModule.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define the 'person' table used in standard tests.
|
||||
*******************************************************************************/
|
||||
@ -196,6 +210,27 @@ public class TestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define the 'shape' table used in standard tests.
|
||||
*******************************************************************************/
|
||||
public static QTableMetaData defineTableShape()
|
||||
{
|
||||
return new QTableMetaData()
|
||||
.withName(TABLE_NAME_SHAPE)
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||
.withField(new QFieldMetaData("name", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("type", QFieldType.STRING)) // todo PVS
|
||||
.withField(new QFieldMetaData("noOfSides", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("isPolygon", QFieldType.BOOLEAN)) // mmm, should be derived from type, no?
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define a 2nd version of the 'person' table for this test (pretend it's backed by a file)
|
||||
*******************************************************************************/
|
||||
|
Reference in New Issue
Block a user