diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..0ef02745 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,77 @@ +version: 2.1 + +executors: + java17: + docker: + - image: 'cimg/openjdk:17.0' + resource_class: small + +orbs: + slack: circleci/slack@4.10.1 + +commands: + run_maven: + parameters: + maven_subcommand: + default: test + type: string + steps: + - checkout + - restore_cache: + keys: + - v1-dependencies-{{ checksum "pom.xml" }} + - run: + name: Run Maven + command: | + mvn -s .circleci/mvn-settings.xml << parameters.maven_subcommand >> + - run: + name: Save test results + command: | + mkdir -p ~/test-results/junit/ + find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/test-results/junit/ \; + when: always + - store_test_results: + path: ~/test-results + - save_cache: + paths: + - ~/.m2 + key: v1-dependencies-{{ checksum "pom.xml" }} + +jobs: + mvn_test: + executor: java17 + steps: + - run_maven: + maven_subcommand: test + - slack/notify: + event: fail + + mvn_deploy: + executor: java17 + steps: + - run_maven: + maven_subcommand: deploy + - slack/notify: + event: always + +workflows: + test_only: + jobs: + - mvn_test: + context: [ qqq-maven-registry-credentials, kingsrook-slack ] + filters: + branches: + ignore: /dev/ + tags: + ignore: /(version|snapshot)-.*/ + + deploy: + jobs: + - mvn_deploy: + context: [ qqq-maven-registry-credentials, kingsrook-slack ] + filters: + branches: + only: /dev/ + tags: + only: /(version|snapshot)-.*/ + diff --git a/.circleci/mvn-settings.xml b/.circleci/mvn-settings.xml new file mode 100644 index 00000000..b2a345f0 --- /dev/null +++ b/.circleci/mvn-settings.xml @@ -0,0 +1,9 @@ + + + + github-qqq-maven-registry + ${env.QQQ_MAVEN_REGISTRY_USERNAME} + ${env.QQQ_MAVEN_REGISTRY_PASSWORD} + + + diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml deleted file mode 100644 index 80d00dfd..00000000 --- a/.github/workflows/maven.yml +++ /dev/null @@ -1,35 +0,0 @@ -# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven - -name: Java CI with Maven - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 17 - uses: actions/setup-java@v2 - with: - java-version: '17' - distribution: 'adopt' - cache: maven - - name: maven-settings-xml-action - uses: whelk-io/maven-settings-xml-action@v20 - with: - servers: '[{ "id": "github-qqq-maven-registry", "username": "${{ secrets.QQQ_MAVEN_REGISTRY_USERNAME }}", "password": "${{ secrets.QQQ_MAVEN_REGISTRY_PASSWORD }}" }]' - repositories: '[{ "id": "github-qqq-maven-registry", "url": "https://maven.pkg.github.com/Kingsrook/qqq-maven-registry", "snapshots": { "enabled": "true" }}]' - - name: Build with Maven - run: mvn -B package --file pom.xml - - name: Publish to GitHub Packages Apache Maven - run: mvn deploy - env: - GITHUB_TOKEN: ${{ github.token }} diff --git a/README.md b/README.md index cffb2382..3920b9a6 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ This is a qqq middleware module, providing [javalin](https://javalin.io) access QQQ - Low-code Application Framework for Engineers. \ Copyright (C) 2022. Kingsrook, LLC \ 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States \ -contact@kingsrook.com -https://github.com/Kingsrook/intellij-commentator-plugin +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 diff --git a/checkstyle.xml b/checkstyle.xml index dbaa3479..76f872ed 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -46,6 +46,7 @@ --> + @@ -171,7 +172,7 @@ - + - + 4.0.0 com.kingsrook.qqq qqq-middleware-javalin - 0.0-SNAPSHOT + 0.0.0 + + + scm:git:git@github.com:Kingsrook/qqq-middleware-javalin.git + scm:git:git@github.com:Kingsrook/qqq-middleware-javalin.git + HEAD + @@ -47,12 +51,12 @@ com.kingsrook.qqq qqq-backend-core - 0.0-SNAPSHOT + 0.0.0 com.kingsrook.qqq qqq-backend-module-rdbms - 0.0-SNAPSHOT + 0.0.0 test @@ -71,23 +75,13 @@ com.h2database h2 - 1.4.197 + 2.1.210 test org.slf4j slf4j-simple - 1.7.30 - - - org.slf4j - slf4j-api - 1.7.30 - - - org.slf4j - slf4j-simple - 1.7.30 + 1.7.36 @@ -99,17 +93,17 @@ org.apache.logging.log4j log4j-api - 2.15.0 + 2.17.2 org.apache.logging.log4j log4j-core - 2.15.0 + 2.17.2 org.junit.jupiter junit-jupiter-engine - 5.8.1 + 5.8.2 test @@ -165,13 +159,28 @@ + + com.amashchenko.maven.plugin + gitflow-maven-plugin + 1.18.0 + + + main + dev + version- + + true + install + true + 1 + + - - github + github-qqq-maven-registry GitHub QQQ Maven Registry https://maven.pkg.github.com/Kingsrook/qqq-maven-registry diff --git a/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java b/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java index ca33f553..6c68564d 100644 --- a/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java +++ b/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java @@ -29,13 +29,19 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import com.kingsrook.qqq.backend.core.actions.DeleteAction; import com.kingsrook.qqq.backend.core.actions.InsertAction; import com.kingsrook.qqq.backend.core.actions.MetaDataAction; import com.kingsrook.qqq.backend.core.actions.QueryAction; +import com.kingsrook.qqq.backend.core.actions.RunProcessAction; import com.kingsrook.qqq.backend.core.actions.TableMetaDataAction; import com.kingsrook.qqq.backend.core.actions.UpdateAction; import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter; +import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException; import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException; import com.kingsrook.qqq.backend.core.model.actions.AbstractQRequest; @@ -47,17 +53,21 @@ import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataRequest; import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataResult; import com.kingsrook.qqq.backend.core.model.actions.metadata.table.TableMetaDataRequest; import com.kingsrook.qqq.backend.core.model.actions.metadata.table.TableMetaDataResult; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessRequest; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessResult; import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest; import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult; import com.kingsrook.qqq.backend.core.model.actions.update.UpdateRequest; import com.kingsrook.qqq.backend.core.model.actions.update.UpdateResult; import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.core.modules.QAuthenticationModuleDispatcher; import com.kingsrook.qqq.backend.core.modules.interfaces.QAuthenticationModuleInterface; +import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.ExceptionUtils; import com.kingsrook.qqq.backend.core.utils.JsonUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils; @@ -84,6 +94,7 @@ import static io.javalin.apibuilder.ApiBuilder.put; public class QJavalinImplementation { private static final Logger LOG = LogManager.getLogger(QJavalinImplementation.class); + private static final int SESSION_COOKIE_AGE = 60 * 60 * 24; private static QInstance qInstance; @@ -154,6 +165,7 @@ public class QJavalinImplementation path("/:table", () -> { get("", QJavalinImplementation::tableMetaData); + // todo - process meta data - just under tables? or top-level too? maybe move tables to be under /tables/? }); }); path("/data", () -> @@ -172,6 +184,14 @@ public class QJavalinImplementation }); }); }); + path("/processes", () -> + { + path("/:process", () -> + { + get("/init", QJavalinImplementation::processInit); + get("/step", QJavalinImplementation::processStep); + }); + }); }); } @@ -183,7 +203,7 @@ public class QJavalinImplementation private static void setupSession(Context context, AbstractQRequest request) throws QModuleDispatchException { QAuthenticationModuleDispatcher qAuthenticationModuleDispatcher = new QAuthenticationModuleDispatcher(); - QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(request.getAuthenticationMetaData()); + QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(request.getAuthenticationMetaData()); // todo - does this need some per-provider logic actually? mmm... Map authenticationContext = new HashMap<>(); @@ -203,7 +223,7 @@ public class QJavalinImplementation { try { - String table = context.pathParam("table"); + String table = context.pathParam("table"); List primaryKeys = new ArrayList<>(); primaryKeys.add(context.pathParam("primaryKey")); @@ -232,9 +252,9 @@ public class QJavalinImplementation { try { - String table = context.pathParam("table"); + String table = context.pathParam("table"); List recordList = new ArrayList<>(); - QRecord record = new QRecord(); + QRecord record = new QRecord(); record.setTableName(table); recordList.add(record); @@ -276,9 +296,9 @@ public class QJavalinImplementation { try { - String table = context.pathParam("table"); + String table = context.pathParam("table"); List recordList = new ArrayList<>(); - QRecord record = new QRecord(); + QRecord record = new QRecord(); record.setTableName(table); recordList.add(record); @@ -422,7 +442,6 @@ public class QJavalinImplementation else { LOG.warn("Exception in javalin request", e); - e.printStackTrace(); context.status(HttpStatus.INTERNAL_SERVER_ERROR_500) .result("{\"error\":\"" + e.getClass().getSimpleName() + " (" + e.getMessage() + ")\"}"); } @@ -462,4 +481,88 @@ public class QJavalinImplementation return (null); } + + + + /******************************************************************************* + ** Init a process (named in path param :process) + ** + *******************************************************************************/ + private static void processInit(Context context) throws QException + { + RunProcessRequest runProcessRequest = new RunProcessRequest(qInstance); + setupSession(context, runProcessRequest); + runProcessRequest.setProcessName(context.pathParam("process")); + runProcessRequest.setCallback(new QJavalinProcessCallback()); + + ///////////////////////////////////////////////////////////////////////////////////// + // take values from query-string params, and put them into the run process request // + // todo - better from POST body, or with a "field-" type of prefix?? // + ///////////////////////////////////////////////////////////////////////////////////// + for(Map.Entry> queryParam : context.queryParamMap().entrySet()) + { + String fieldName = queryParam.getKey(); + List values = queryParam.getValue(); + if(CollectionUtils.nullSafeHasContents(values)) + { + runProcessRequest.addValue(fieldName, values.get(0)); + } + } + + try + { + //////////////////////////////////////////////// + // run the process // + // todo - some "job id" to return to caller? // + //////////////////////////////////////////////// + CompletableFuture future = CompletableFuture.supplyAsync(() -> + { + try + { + LOG.info("Running process [" + runProcessRequest.getProcessName() + "]"); + RunProcessResult runProcessResult = new RunProcessAction().execute(runProcessRequest); + LOG.info("Process result error? " + runProcessResult.getError()); + for(QFieldMetaData outputField : qInstance.getProcess(runProcessRequest.getProcessName()).getOutputFields()) + { + LOG.info("Process result output value: " + outputField.getName() + ": " + runProcessResult.getValues().get(outputField.getName())); + } + return (runProcessResult); + } + catch(Exception e) + { + LOG.error("Error running future for process", e); + throw (new CompletionException(e)); + } + }); + + Map resultForCaller = new HashMap<>(); + try + { + RunProcessResult runProcessResult = future.get(3, TimeUnit.SECONDS); + resultForCaller.put("error", runProcessResult.getError()); + resultForCaller.put("values", runProcessResult.getValues()); + } + catch(TimeoutException te) + { + resultForCaller.put("jobId", "Job is running asynchronously... job id available in a later version."); + } + context.result(JsonUtils.toJson(resultForCaller)); + } + catch(Exception e) + { + handleException(context, e); + } + } + + + + /******************************************************************************* + ** Run a step in a process (named in path param :process) + ** + *******************************************************************************/ + private static void processStep(Context context) + { + + } + } diff --git a/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessCallback.java b/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessCallback.java new file mode 100644 index 00000000..328a66b2 --- /dev/null +++ b/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessCallback.java @@ -0,0 +1,66 @@ +/* + * 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.javalin; + + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback; +import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter; +import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class QJavalinProcessCallback implements QProcessCallback +{ + private static final Logger LOG = LogManager.getLogger(QJavalinProcessCallback.class); + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public QQueryFilter getQueryFilter() + { + LOG.warn("Getting a query filter in javalin is NOT yet implemented"); + return (new QQueryFilter()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public Map getFieldValues(List fields) + { + LOG.warn("Getting field values in javalin is NOT yet implemented"); + return (new HashMap<>()); + } +} diff --git a/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementationTest.java b/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementationTest.java index 5f17251a..f8376c5c 100644 --- a/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementationTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementationTest.java @@ -203,6 +203,7 @@ class QJavalinImplementationTest Map body = new HashMap<>(); body.put("firstName", "Bobby"); body.put("lastName", "Hull"); + body.put("email", "bobby@hull.com"); HttpResponse response = Unirest.post(BASE_URL + "/data/person") .header("Content-Type", "application/json") @@ -284,4 +285,43 @@ class QJavalinImplementationTest assertEquals(4, rowsFound); })); } + + + + /******************************************************************************* + ** test running a process + ** + *******************************************************************************/ + @Test + public void test_processGreetInit() throws Exception + { + HttpResponse response = Unirest.get(BASE_URL + "/processes/greet/init") + .header("Content-Type", "application/json") + .asString(); + + assertEquals(200, response.getStatus()); + JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody()); + assertNotNull(jsonObject); + assertEquals("null X null", jsonObject.getJSONObject("values").getString("outputMessage")); + } + + + + /******************************************************************************* + ** test running a process with field values on the query string + ** + *******************************************************************************/ + @Test + public void test_processGreetInitWithQueryValues() throws Exception + { + HttpResponse response = Unirest.get(BASE_URL + "/processes/greet/init?greetingPrefix=Hey&greetingSuffix=Jude") + .header("Content-Type", "application/json") + .asString(); + + assertEquals(200, response.getStatus()); + JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody()); + assertNotNull(jsonObject); + assertEquals("Hey X Jude", jsonObject.getJSONObject("values").getString("outputMessage")); + } + } diff --git a/src/test/java/com/kingsrook/qqq/backend/javalin/TestUtils.java b/src/test/java/com/kingsrook/qqq/backend/javalin/TestUtils.java index 6d3c4e8f..a25a5104 100644 --- a/src/test/java/com/kingsrook/qqq/backend/javalin/TestUtils.java +++ b/src/test/java/com/kingsrook/qqq/backend/javalin/TestUtils.java @@ -25,13 +25,23 @@ package com.kingsrook.qqq.backend.javalin; import java.io.InputStream; import java.sql.Connection; import java.util.List; +import com.kingsrook.qqq.backend.core.interfaces.mock.MockFunctionBody; import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData; -import com.kingsrook.qqq.backend.core.model.metadata.QInstance; -import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference; +import com.kingsrook.qqq.backend.core.model.metadata.QCodeType; +import com.kingsrook.qqq.backend.core.model.metadata.QCodeUsage; import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QFieldType; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; -import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionOutputMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QOutputView; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListView; +import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData; import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; import org.apache.commons.io.IOUtils; @@ -52,11 +62,12 @@ public class TestUtils @SuppressWarnings("unchecked") public static void primeTestDatabase() throws Exception { - ConnectionManager connectionManager = new ConnectionManager(); - Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(TestUtils.defineBackend())); - InputStream primeTestDatabaseSqlStream = TestUtils.class.getResourceAsStream("/prime-test-database.sql"); + ConnectionManager connectionManager = new ConnectionManager(); + Connection connection = connectionManager.getConnection(TestUtils.defineBackend()); + InputStream primeTestDatabaseSqlStream = TestUtils.class.getResourceAsStream("/prime-test-database.sql"); assertNotNull(primeTestDatabaseSqlStream); List lines = (List) IOUtils.readLines(primeTestDatabaseSqlStream); + lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList(); String joinedSQL = String.join("\n", lines); for(String sql : joinedSQL.split(";")) { @@ -73,7 +84,7 @@ public class TestUtils public static void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception { ConnectionManager connectionManager = new ConnectionManager(); - Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(defineBackend())); + Connection connection = connectionManager.getConnection(defineBackend()); QueryManager.executeStatement(connection, sql, resultSetProcessor); } @@ -89,6 +100,7 @@ public class TestUtils qInstance.setAuthentication(defineAuthentication()); qInstance.addBackend(defineBackend()); qInstance.addTable(defineTablePerson()); + qInstance.addProcess(defineProcessGreetPeople()); return (qInstance); } @@ -111,16 +123,16 @@ public class TestUtils ** Define the h2 rdbms backend ** *******************************************************************************/ - public static QBackendMetaData defineBackend() + public static RDBMSBackendMetaData defineBackend() { - return new QBackendMetaData() - .withName("default") - .withType("rdbms") - .withValue("vendor", "h2") - .withValue("hostName", "mem") - .withValue("databaseName", "test_database") - .withValue("username", "sa") - .withValue("password", ""); + RDBMSBackendMetaData rdbmsBackendMetaData = new RDBMSBackendMetaData() + .withVendor("h2") + .withHostName("mem") + .withDatabaseName("test_database") + .withUsername("sa") + .withPassword(""); + rdbmsBackendMetaData.setName("default"); + return (rdbmsBackendMetaData); } @@ -145,4 +157,38 @@ public class TestUtils .withField(new QFieldMetaData("email", QFieldType.STRING)); } + + + /******************************************************************************* + ** Define the 'greet people' process + *******************************************************************************/ + private static QProcessMetaData defineProcessGreetPeople() + { + return new QProcessMetaData() + .withName("greet") + .withTableName("person") + .addFunction(new QFunctionMetaData() + .withName("prepare") + .withCode(new QCodeReference() + .withName(MockFunctionBody.class.getName()) + .withCodeType(QCodeType.JAVA) + .withCodeUsage(QCodeUsage.FUNCTION)) // todo - needed, or implied in this context? + .withInputData(new QFunctionInputMetaData() + .withRecordListMetaData(new QRecordListMetaData().withTableName("person")) + .withFieldList(List.of( + new QFieldMetaData("greetingPrefix", QFieldType.STRING), + new QFieldMetaData("greetingSuffix", QFieldType.STRING) + ))) + .withOutputMetaData(new QFunctionOutputMetaData() + .withRecordListMetaData(new QRecordListMetaData() + .withTableName("person") + .addField(new QFieldMetaData("fullGreeting", QFieldType.STRING)) + ) + .withFieldList(List.of(new QFieldMetaData("outputMessage", QFieldType.STRING)))) + .withOutputView(new QOutputView() + .withMessageField("outputMessage") + .withRecordListView(new QRecordListView().withFieldNames(List.of("id", "firstName", "lastName", "fullGreeting")))) + ); + } + } diff --git a/src/test/resources/prime-test-database.sql b/src/test/resources/prime-test-database.sql index 6227f249..be858987 100644 --- a/src/test/resources/prime-test-database.sql +++ b/src/test/resources/prime-test-database.sql @@ -1,28 +1,28 @@ -/* - * 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 . - */ +-- +-- 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 . +-- DROP TABLE IF EXISTS person; CREATE TABLE person ( - id SERIAL, + id INT AUTO_INCREMENT, create_date TIMESTAMP DEFAULT now(), modify_date TIMESTAMP DEFAULT now(),