From 52121cc4f327bc9b41b54524ad4f650c298b99b5 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 12 Aug 2022 11:39:39 -0500 Subject: [PATCH] Adding POST_QUERY_RECORD customizer; formalizing customizers a bit more --- .../actions/customizers/CustomizerLoader.java | 96 +++++++++++++ .../core/actions/customizers/Customizers.java | 32 +++++ .../QInstanceValidationException.java | 2 +- .../actions/tables/query/QueryOutput.java | 31 +++++ .../model/metadata/tables/QTableMetaData.java | 7 +- .../memory/MemoryBackendModuleTest.java | 126 ++++++++++++++---- .../FilesystemBackendModuleInterface.java | 2 +- .../actions/AbstractBaseFilesystemAction.java | 13 +- .../base/actions/FilesystemCustomizers.java | 35 +++++ .../local/FilesystemBackendModule.java | 12 ++ .../local/actions/FilesystemCountAction.java | 55 ++++++++ .../actions/FilesystemQueryActionTest.java | 12 +- 12 files changed, 378 insertions(+), 45 deletions(-) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/CustomizerLoader.java create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/Customizers.java create mode 100644 qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/actions/FilesystemCustomizers.java create mode 100644 qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/local/actions/FilesystemCountAction.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/CustomizerLoader.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/CustomizerLoader.java new file mode 100644 index 00000000..d0ef11d8 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/CustomizerLoader.java @@ -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 . + */ + +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 codeReference = table.getCustomizer(customizerName); + if(codeReference.isPresent()) + { + return (CustomizerLoader.getFunction(codeReference.get())); + } + + return null; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @SuppressWarnings("unchecked") + public static Function 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) 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); + } + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/Customizers.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/Customizers.java new file mode 100644 index 00000000..4da39af3 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/Customizers.java @@ -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 . + */ + +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"; + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/exceptions/QInstanceValidationException.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/exceptions/QInstanceValidationException.java index 311ec0a8..fa090ca1 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/exceptions/QInstanceValidationException.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/exceptions/QInstanceValidationException.java @@ -57,7 +57,7 @@ public class QInstanceValidationException extends QException { super( (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"); if(reasons != null && reasons.size() > 0) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryOutput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryOutput.java index a9e19342..96340a69 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryOutput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryOutput.java @@ -24,8 +24,13 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query; import java.io.Serializable; 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.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 { + private static final Logger LOG = LogManager.getLogger(QueryOutput.class); + private QueryOutputStorageInterface storage; + private Function postQueryRecordCustomizer; + /******************************************************************************* @@ -52,6 +61,8 @@ public class QueryOutput extends AbstractActionOutput implements Serializable { storage = new QueryOutputList(); } + + postQueryRecordCustomizer = (Function) CustomizerLoader.getTableCustomizerFunction(queryInput.getTable(), Customizers.POST_QUERY_RECORD); } @@ -65,16 +76,36 @@ public class QueryOutput extends AbstractActionOutput implements Serializable *******************************************************************************/ public void addRecord(QRecord record) { + record = runPostQueryRecordCustomizer(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 *******************************************************************************/ public void addRecords(List records) { + if(this.postQueryRecordCustomizer != null) + { + records.replaceAll(t -> this.postQueryRecordCustomizer.apply(t)); + } + storage.addRecords(records); } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/QTableMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/QTableMetaData.java index 0fec3d48..2f920886 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/QTableMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/QTableMetaData.java @@ -408,12 +408,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable } QCodeReference function = customizers.get(customizerName); - if(function == null) - { - throw (new IllegalArgumentException("Customizer [" + customizerName + "] was not found in table [" + name + "].")); - } - - return (Optional.of(function)); + return (Optional.ofNullable(function)); } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryBackendModuleTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryBackendModuleTest.java index f07e1b9a..ec1b2673 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryBackendModuleTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryBackendModuleTest.java @@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.memory; 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.DeleteAction; 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.data.QRecord; 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.session.QSession; import com.kingsrook.qqq.backend.core.utils.TestUtils; @@ -90,28 +94,9 @@ class MemoryBackendModuleTest 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) - )); + insertInput.setRecords(getTestRecords(table)); 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().anyMatch(r -> r.getValueInteger("id").equals(1))); assertTrue(insertOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2))); @@ -124,7 +109,7 @@ class MemoryBackendModuleTest queryInput.setSession(session); queryInput.setTableName(table.getName()); 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().anyMatch(r -> r.getValueInteger("id").equals(1))); assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(2))); @@ -152,10 +137,10 @@ class MemoryBackendModuleTest .withValue("type", "ellipse") )); UpdateOutput updateOutput = new UpdateAction().execute(updateInput); - assertEquals(updateOutput.getRecords().size(), 2); + assertEquals(2, updateOutput.getRecords().size()); 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().anyMatch(r -> r.getValueString("name").equals("Not My Triangle any more"))); assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueString("type").equals("ellipse"))); @@ -171,15 +156,104 @@ class MemoryBackendModuleTest deleteInput.setTableName(table.getName()); deleteInput.setPrimaryKeys(List.of(1, 2)); DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput); - assertEquals(deleteOutput.getDeletedRecordCount(), 2); + assertEquals(2, deleteOutput.getDeletedRecordCount()); assertEquals(1, new CountAction().execute(countInput).getCount()); 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(2))); assertTrue(queryOutput.getRecords().stream().anyMatch(r -> r.getValueInteger("id").equals(3))); } + + + /******************************************************************************* + ** + *******************************************************************************/ + private List 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 + { + static int executionCount = 0; + + + + @Override + public QRecord apply(QRecord record) + { + executionCount++; + record.setValue("tenTimesId", record.getValueInteger("id") * 10); + return (record); + } + } } \ No newline at end of file diff --git a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/FilesystemBackendModuleInterface.java b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/FilesystemBackendModuleInterface.java index 23c38c46..816caa06 100644 --- a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/FilesystemBackendModuleInterface.java +++ b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/FilesystemBackendModuleInterface.java @@ -31,11 +31,11 @@ import com.kingsrook.qqq.backend.module.filesystem.base.actions.AbstractBaseFile *******************************************************************************/ public interface FilesystemBackendModuleInterface { - String CUSTOMIZER_FILE_POST_FILE_READ = "postFileRead"; /******************************************************************************* ** For filesystem backends, get the module-specific action base-class, that helps ** with functions like listing and deleting files. *******************************************************************************/ AbstractBaseFilesystemAction getActionBase(); + } diff --git a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/actions/AbstractBaseFilesystemAction.java b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/actions/AbstractBaseFilesystemAction.java index 02b26622..5f846dea 100644 --- a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/actions/AbstractBaseFilesystemAction.java +++ b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/actions/AbstractBaseFilesystemAction.java @@ -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.QTableMetaData; 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.model.metadata.AbstractFilesystemBackendMetaData; import com.kingsrook.qqq.backend.module.filesystem.base.model.metadata.AbstractFilesystemTableBackendDetails; @@ -203,7 +202,15 @@ public abstract class AbstractBaseFilesystemAction 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 { @@ -281,7 +288,7 @@ public abstract class AbstractBaseFilesystemAction *******************************************************************************/ private String customizeFileContentsAfterReading(QTableMetaData table, String fileContents) throws QException { - Optional optionalCustomizer = table.getCustomizer(FilesystemBackendModuleInterface.CUSTOMIZER_FILE_POST_FILE_READ); + Optional optionalCustomizer = table.getCustomizer(FilesystemCustomizers.POST_READ_FILE); if(optionalCustomizer.isEmpty()) { return (fileContents); diff --git a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/actions/FilesystemCustomizers.java b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/actions/FilesystemCustomizers.java new file mode 100644 index 00000000..8e2416e0 --- /dev/null +++ b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/base/actions/FilesystemCustomizers.java @@ -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 . + */ + +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"; +} diff --git a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/local/FilesystemBackendModule.java b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/local/FilesystemBackendModule.java index 2bc14244..493a08ac 100644 --- a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/local/FilesystemBackendModule.java +++ b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/local/FilesystemBackendModule.java @@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.module.filesystem.local; 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.InsertInterface; 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.actions.AbstractBaseFilesystemAction; 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.FilesystemInsertAction; 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(); + } + + /******************************************************************************* ** diff --git a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/local/actions/FilesystemCountAction.java b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/local/actions/FilesystemCountAction.java new file mode 100644 index 00000000..586eb9f8 --- /dev/null +++ b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/local/actions/FilesystemCountAction.java @@ -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 . + */ + +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); + } + +} diff --git a/qqq-backend-module-filesystem/src/test/java/com/kingsrook/qqq/backend/module/filesystem/local/actions/FilesystemQueryActionTest.java b/qqq-backend-module-filesystem/src/test/java/com/kingsrook/qqq/backend/module/filesystem/local/actions/FilesystemQueryActionTest.java index 90771e43..be40e86a 100644 --- a/qqq-backend-module-filesystem/src/test/java/com/kingsrook/qqq/backend/module/filesystem/local/actions/FilesystemQueryActionTest.java +++ b/qqq-backend-module-filesystem/src/test/java/com/kingsrook/qqq/backend/module/filesystem/local/actions/FilesystemQueryActionTest.java @@ -26,14 +26,13 @@ import java.util.function.Function; 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.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.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.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.actions.FilesystemCustomizers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -72,10 +71,7 @@ public class FilesystemQueryActionTest extends FilesystemActionTest QInstance instance = TestUtils.defineInstance(); QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_LOCAL_FS_JSON); - table.withCustomizer(FilesystemBackendModuleInterface.CUSTOMIZER_FILE_POST_FILE_READ, new QCodeReference() - .withName(ValueUpshifter.class.getName()) - .withCodeType(QCodeType.JAVA) - .withCodeUsage(QCodeUsage.CUSTOMIZER)); + table.withCustomizer(FilesystemCustomizers.POST_READ_FILE, new QCodeReference(ValueUpshifter.class, QCodeUsage.CUSTOMIZER)); queryInput.setInstance(instance); queryInput.setTableName(TestUtils.defineLocalFilesystemJSONPersonTable().getName());