Adding POST_QUERY_RECORD customizer; formalizing customizers a bit more

This commit is contained in:
2022-08-12 11:39:39 -05:00
parent 965bc5bf29
commit 52121cc4f3
12 changed files with 378 additions and 45 deletions

View File

@ -0,0 +1,96 @@
/*
* 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.actions.customizers;
import java.util.Optional;
import java.util.function.Function;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/*******************************************************************************
** Utility to load code for running QQQ customizers.
*******************************************************************************/
public class CustomizerLoader
{
private static final Logger LOG = LogManager.getLogger(CustomizerLoader.class);
/*******************************************************************************
**
*******************************************************************************/
public static Function<?, ?> getTableCustomizerFunction(QTableMetaData table, String customizerName)
{
Optional<QCodeReference> codeReference = table.getCustomizer(customizerName);
if(codeReference.isPresent())
{
return (CustomizerLoader.getFunction(codeReference.get()));
}
return null;
}
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public static <T, R> Function<T, R> getFunction(QCodeReference codeReference)
{
if(codeReference == null)
{
return (null);
}
if(!codeReference.getCodeType().equals(QCodeType.JAVA))
{
///////////////////////////////////////////////////////////////////////////////////////
// todo - 1) support more languages, 2) wrap them w/ java Functions here, 3) profit! //
///////////////////////////////////////////////////////////////////////////////////////
throw (new IllegalArgumentException("Only JAVA customizers are supported at this time."));
}
try
{
Class<?> customizerClass = Class.forName(codeReference.getName());
return ((Function<T, R>) customizerClass.getConstructor().newInstance());
}
catch(Exception e)
{
LOG.error("Error initializing customizer: " + codeReference);
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// return null here - under the assumption that during normal run-time operations, we'll never hit here //
// as we'll want to validate all functions in the instance validator at startup time (and IT will throw //
// if it finds an invalid code reference //
//////////////////////////////////////////////////////////////////////////////////////////////////////////
return (null);
}
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.actions.customizers;
/*******************************************************************************
** Standard place where the names of QQQ Customization points are defined.
*******************************************************************************/
public interface Customizers
{
String POST_QUERY_RECORD = "postQueryRecord";
}

View File

@ -57,7 +57,7 @@ public class QInstanceValidationException extends QException
{ {
super( super(
(reasons != null && reasons.size() > 0) (reasons != null && reasons.size() > 0)
? "Instance validation failed for the following reasons: " + StringUtils.joinWithCommasAndAnd(reasons) ? "Instance validation failed for the following reasons:\n - " + StringUtils.join("\n - ", reasons)
: "Validation failed, but no reasons were provided"); : "Validation failed, but no reasons were provided");
if(reasons != null && reasons.size() > 0) if(reasons != null && reasons.size() > 0)

View File

@ -24,8 +24,13 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.function.Function;
import com.kingsrook.qqq.backend.core.actions.customizers.CustomizerLoader;
import com.kingsrook.qqq.backend.core.actions.customizers.Customizers;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/******************************************************************************* /*******************************************************************************
@ -34,8 +39,12 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
*******************************************************************************/ *******************************************************************************/
public class QueryOutput extends AbstractActionOutput implements Serializable public class QueryOutput extends AbstractActionOutput implements Serializable
{ {
private static final Logger LOG = LogManager.getLogger(QueryOutput.class);
private QueryOutputStorageInterface storage; private QueryOutputStorageInterface storage;
private Function<QRecord, QRecord> postQueryRecordCustomizer;
/******************************************************************************* /*******************************************************************************
@ -52,6 +61,8 @@ public class QueryOutput extends AbstractActionOutput implements Serializable
{ {
storage = new QueryOutputList(); storage = new QueryOutputList();
} }
postQueryRecordCustomizer = (Function<QRecord, QRecord>) CustomizerLoader.getTableCustomizerFunction(queryInput.getTable(), Customizers.POST_QUERY_RECORD);
} }
@ -65,16 +76,36 @@ public class QueryOutput extends AbstractActionOutput implements Serializable
*******************************************************************************/ *******************************************************************************/
public void addRecord(QRecord record) public void addRecord(QRecord record)
{ {
record = runPostQueryRecordCustomizer(record);
storage.addRecord(record); storage.addRecord(record);
} }
/*******************************************************************************
**
*******************************************************************************/
public QRecord runPostQueryRecordCustomizer(QRecord record)
{
if(this.postQueryRecordCustomizer != null)
{
record = this.postQueryRecordCustomizer.apply(record);
}
return record;
}
/******************************************************************************* /*******************************************************************************
** add a list of records to this output ** add a list of records to this output
*******************************************************************************/ *******************************************************************************/
public void addRecords(List<QRecord> records) public void addRecords(List<QRecord> records)
{ {
if(this.postQueryRecordCustomizer != null)
{
records.replaceAll(t -> this.postQueryRecordCustomizer.apply(t));
}
storage.addRecords(records); storage.addRecords(records);
} }

View File

@ -408,12 +408,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
} }
QCodeReference function = customizers.get(customizerName); QCodeReference function = customizers.get(customizerName);
if(function == null) return (Optional.ofNullable(function));
{
throw (new IllegalArgumentException("Customizer [" + customizerName + "] was not found in table [" + name + "]."));
}
return (Optional.of(function));
} }

View File

@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.memory;
import java.util.List; import java.util.List;
import java.util.function.Function;
import com.kingsrook.qqq.backend.core.actions.customizers.Customizers;
import com.kingsrook.qqq.backend.core.actions.tables.CountAction; 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.DeleteAction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
@ -40,6 +42,8 @@ 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.actions.tables.update.UpdateOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord; 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.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; 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.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.TestUtils; import com.kingsrook.qqq.backend.core.utils.TestUtils;
@ -90,28 +94,9 @@ class MemoryBackendModuleTest
InsertInput insertInput = new InsertInput(qInstance); InsertInput insertInput = new InsertInput(qInstance);
insertInput.setSession(session); insertInput.setSession(session);
insertInput.setTableName(table.getName()); insertInput.setTableName(table.getName());
insertInput.setRecords(List.of( insertInput.setRecords(getTestRecords(table));
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); InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals(insertOutput.getRecords().size(), 3); assertEquals(3, insertOutput.getRecords().size());
assertTrue(insertOutput.getRecords().stream().allMatch(r -> r.getValue("id") != null)); 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(1)));
assertTrue(insertOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2))); assertTrue(insertOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2)));
@ -124,7 +109,7 @@ class MemoryBackendModuleTest
queryInput.setSession(session); queryInput.setSession(session);
queryInput.setTableName(table.getName()); queryInput.setTableName(table.getName());
QueryOutput queryOutput = new QueryAction().execute(queryInput); QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(queryOutput.getRecords().size(), 3); assertEquals(3, queryOutput.getRecords().size());
assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValue("id") != null)); 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(1)));
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2))); assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2)));
@ -152,10 +137,10 @@ class MemoryBackendModuleTest
.withValue("type", "ellipse") .withValue("type", "ellipse")
)); ));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput); UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals(updateOutput.getRecords().size(), 2); assertEquals(2, updateOutput.getRecords().size());
queryOutput = new QueryAction().execute(queryInput); queryOutput = new QueryAction().execute(queryInput);
assertEquals(queryOutput.getRecords().size(), 3); assertEquals(3, queryOutput.getRecords().size());
assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("name").equals("My Triangle"))); 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("name").equals("Not My Triangle any more")));
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueString("type").equals("ellipse"))); assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueString("type").equals("ellipse")));
@ -171,15 +156,104 @@ class MemoryBackendModuleTest
deleteInput.setTableName(table.getName()); deleteInput.setTableName(table.getName());
deleteInput.setPrimaryKeys(List.of(1, 2)); deleteInput.setPrimaryKeys(List.of(1, 2));
DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput); DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(deleteOutput.getDeletedRecordCount(), 2); assertEquals(2, deleteOutput.getDeletedRecordCount());
assertEquals(1, new CountAction().execute(countInput).getCount()); assertEquals(1, new CountAction().execute(countInput).getCount());
queryOutput = new QueryAction().execute(queryInput); queryOutput = new QueryAction().execute(queryInput);
assertEquals(queryOutput.getRecords().size(), 1); assertEquals(1, queryOutput.getRecords().size());
assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueInteger("id").equals(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().noneMatch(r -> r.getValueInteger("id").equals(2)));
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(3))); assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(3)));
} }
/*******************************************************************************
**
*******************************************************************************/
private List<QRecord> getTestRecords(QTableMetaData table)
{
return 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)
);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testCustomizer() throws QException
{
QInstance qInstance = TestUtils.defineInstance();
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_SHAPE);
QSession session = new QSession();
///////////////////////////////////
// add a customizer to the table //
///////////////////////////////////
table.withCustomizer(Customizers.POST_QUERY_RECORD, new QCodeReference(ShapeTestCustomizer.class, QCodeUsage.CUSTOMIZER));
//////////////////
// do an insert //
//////////////////
InsertInput insertInput = new InsertInput(qInstance);
insertInput.setSession(session);
insertInput.setTableName(table.getName());
insertInput.setRecords(getTestRecords(table));
new InsertAction().execute(insertInput);
///////////////////////////////////////////////////////
// do a query - assert that the customizer did stuff //
///////////////////////////////////////////////////////
ShapeTestCustomizer.executionCount = 0;
QueryInput queryInput = new QueryInput(qInstance);
queryInput.setSession(session);
queryInput.setTableName(table.getName());
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(3, queryOutput.getRecords().size());
assertEquals(3, ShapeTestCustomizer.executionCount);
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(1) && r.getValueInteger("tenTimesId").equals(10)));
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2) && r.getValueInteger("tenTimesId").equals(20)));
assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(3) && r.getValueInteger("tenTimesId").equals(30)));
}
/*******************************************************************************
**
*******************************************************************************/
public static class ShapeTestCustomizer implements Function<QRecord, QRecord>
{
static int executionCount = 0;
@Override
public QRecord apply(QRecord record)
{
executionCount++;
record.setValue("tenTimesId", record.getValueInteger("id") * 10);
return (record);
}
}
} }

View File

@ -31,11 +31,11 @@ import com.kingsrook.qqq.backend.module.filesystem.base.actions.AbstractBaseFile
*******************************************************************************/ *******************************************************************************/
public interface FilesystemBackendModuleInterface<FILE> public interface FilesystemBackendModuleInterface<FILE>
{ {
String CUSTOMIZER_FILE_POST_FILE_READ = "postFileRead";
/******************************************************************************* /*******************************************************************************
** For filesystem backends, get the module-specific action base-class, that helps ** For filesystem backends, get the module-specific action base-class, that helps
** with functions like listing and deleting files. ** with functions like listing and deleting files.
*******************************************************************************/ *******************************************************************************/
AbstractBaseFilesystemAction<FILE> getActionBase(); AbstractBaseFilesystemAction<FILE> getActionBase();
} }

View File

@ -40,7 +40,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.module.filesystem.base.FilesystemBackendModuleInterface;
import com.kingsrook.qqq.backend.module.filesystem.base.FilesystemRecordBackendDetailFields; import com.kingsrook.qqq.backend.module.filesystem.base.FilesystemRecordBackendDetailFields;
import com.kingsrook.qqq.backend.module.filesystem.base.model.metadata.AbstractFilesystemBackendMetaData; import com.kingsrook.qqq.backend.module.filesystem.base.model.metadata.AbstractFilesystemBackendMetaData;
import com.kingsrook.qqq.backend.module.filesystem.base.model.metadata.AbstractFilesystemTableBackendDetails; import com.kingsrook.qqq.backend.module.filesystem.base.model.metadata.AbstractFilesystemTableBackendDetails;
@ -203,7 +202,15 @@ public abstract class AbstractBaseFilesystemAction<FILE>
if(queryInput.getRecordPipe() != null) if(queryInput.getRecordPipe() != null)
{ {
new CsvToQRecordAdapter().buildRecordsFromCsv(queryInput.getRecordPipe(), fileContents, table, null, (record -> addBackendDetailsToRecord(record, file))); new CsvToQRecordAdapter().buildRecordsFromCsv(queryInput.getRecordPipe(), fileContents, table, null, (record ->
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// since the CSV adapter is the one responsible for putting records into the pipe (rather than the queryOutput), //
// we must do some of QueryOutput's normal job here - and run the runPostQueryRecordCustomizer //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
addBackendDetailsToRecord(record, file);
queryOutput.runPostQueryRecordCustomizer(record);
}));
} }
else else
{ {
@ -281,7 +288,7 @@ public abstract class AbstractBaseFilesystemAction<FILE>
*******************************************************************************/ *******************************************************************************/
private String customizeFileContentsAfterReading(QTableMetaData table, String fileContents) throws QException private String customizeFileContentsAfterReading(QTableMetaData table, String fileContents) throws QException
{ {
Optional<QCodeReference> optionalCustomizer = table.getCustomizer(FilesystemBackendModuleInterface.CUSTOMIZER_FILE_POST_FILE_READ); Optional<QCodeReference> optionalCustomizer = table.getCustomizer(FilesystemCustomizers.POST_READ_FILE);
if(optionalCustomizer.isEmpty()) if(optionalCustomizer.isEmpty())
{ {
return (fileContents); return (fileContents);

View File

@ -0,0 +1,35 @@
/*
* 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.module.filesystem.base.actions;
import com.kingsrook.qqq.backend.core.actions.customizers.Customizers;
/*******************************************************************************
** Standard place where the names of QQQ Customization points for filesystem-based
** backends are defined.
*******************************************************************************/
public interface FilesystemCustomizers extends Customizers
{
String POST_READ_FILE = "postReadFile";
}

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.module.filesystem.local;
import java.io.File; import java.io.File;
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.DeleteInterface;
import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface; 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.QueryInterface;
@ -33,6 +34,7 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
import com.kingsrook.qqq.backend.module.filesystem.base.FilesystemBackendModuleInterface; import com.kingsrook.qqq.backend.module.filesystem.base.FilesystemBackendModuleInterface;
import com.kingsrook.qqq.backend.module.filesystem.base.actions.AbstractBaseFilesystemAction; import com.kingsrook.qqq.backend.module.filesystem.base.actions.AbstractBaseFilesystemAction;
import com.kingsrook.qqq.backend.module.filesystem.local.actions.AbstractFilesystemAction; import com.kingsrook.qqq.backend.module.filesystem.local.actions.AbstractFilesystemAction;
import com.kingsrook.qqq.backend.module.filesystem.local.actions.FilesystemCountAction;
import com.kingsrook.qqq.backend.module.filesystem.local.actions.FilesystemDeleteAction; import com.kingsrook.qqq.backend.module.filesystem.local.actions.FilesystemDeleteAction;
import com.kingsrook.qqq.backend.module.filesystem.local.actions.FilesystemInsertAction; import com.kingsrook.qqq.backend.module.filesystem.local.actions.FilesystemInsertAction;
import com.kingsrook.qqq.backend.module.filesystem.local.actions.FilesystemQueryAction; import com.kingsrook.qqq.backend.module.filesystem.local.actions.FilesystemQueryAction;
@ -107,6 +109,16 @@ public class FilesystemBackendModule implements QBackendModuleInterface, Filesys
} }
/*******************************************************************************
**
*******************************************************************************/
@Override
public CountInterface getCountInterface()
{
return new FilesystemCountAction();
}
/******************************************************************************* /*******************************************************************************
** **

View File

@ -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.module.filesystem.local.actions;
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;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
/*******************************************************************************
**
*******************************************************************************/
public class FilesystemCountAction extends AbstractFilesystemAction implements CountInterface
{
/*******************************************************************************
**
*******************************************************************************/
public CountOutput execute(CountInput countInput) throws QException
{
QueryInput queryInput = new QueryInput(countInput.getInstance());
queryInput.setSession(countInput.getSession());
queryInput.setTableName(countInput.getTableName());
queryInput.setFilter(countInput.getFilter());
QueryOutput queryOutput = executeQuery(queryInput);
CountOutput countOutput = new CountOutput();
countOutput.setCount(queryOutput.getRecords().size());
return (countOutput);
}
}

View File

@ -26,14 +26,13 @@ import java.util.function.Function;
import com.kingsrook.qqq.backend.core.exceptions.QException; 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.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
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.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.module.filesystem.TestUtils; import com.kingsrook.qqq.backend.module.filesystem.TestUtils;
import com.kingsrook.qqq.backend.module.filesystem.base.FilesystemBackendModuleInterface;
import com.kingsrook.qqq.backend.module.filesystem.base.FilesystemRecordBackendDetailFields; import com.kingsrook.qqq.backend.module.filesystem.base.FilesystemRecordBackendDetailFields;
import com.kingsrook.qqq.backend.module.filesystem.base.actions.FilesystemCustomizers;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -72,10 +71,7 @@ public class FilesystemQueryActionTest extends FilesystemActionTest
QInstance instance = TestUtils.defineInstance(); QInstance instance = TestUtils.defineInstance();
QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_LOCAL_FS_JSON); QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_LOCAL_FS_JSON);
table.withCustomizer(FilesystemBackendModuleInterface.CUSTOMIZER_FILE_POST_FILE_READ, new QCodeReference() table.withCustomizer(FilesystemCustomizers.POST_READ_FILE, new QCodeReference(ValueUpshifter.class, QCodeUsage.CUSTOMIZER));
.withName(ValueUpshifter.class.getName())
.withCodeType(QCodeType.JAVA)
.withCodeUsage(QCodeUsage.CUSTOMIZER));
queryInput.setInstance(instance); queryInput.setInstance(instance);
queryInput.setTableName(TestUtils.defineLocalFilesystemJSONPersonTable().getName()); queryInput.setTableName(TestUtils.defineLocalFilesystemJSONPersonTable().getName());