mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Merge branch 'release/0.0.0'
This commit is contained in:
77
.circleci/config.yml
Normal file
77
.circleci/config.yml
Normal file
@ -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)-.*/
|
||||
|
9
.circleci/mvn-settings.xml
Normal file
9
.circleci/mvn-settings.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<servers>
|
||||
<server>
|
||||
<id>github-qqq-maven-registry</id>
|
||||
<username>${env.QQQ_MAVEN_REGISTRY_USERNAME}</username>
|
||||
<password>${env.QQQ_MAVEN_REGISTRY_PASSWORD}</password>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
35
.github/workflows/maven.yml
vendored
35
.github/workflows/maven.yml
vendored
@ -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 }}
|
@ -6,8 +6,7 @@ This is a qqq middleware module, providing [picocli](https://picocli.info) acces
|
||||
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
|
||||
|
@ -46,6 +46,7 @@
|
||||
-->
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressWarningsHolder"/>
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
@ -171,7 +172,7 @@
|
||||
<property name="caseIndent" value="3"/>
|
||||
<property name="throwsIndent" value="6"/>
|
||||
<property name="lineWrappingIndentation" value="3"/>
|
||||
<property name="arrayInitIndent" value="2"/>
|
||||
<property name="arrayInitIndent" value="6"/>
|
||||
</module>
|
||||
<!--
|
||||
<module name="AbbreviationAsWordInName">
|
||||
@ -260,4 +261,5 @@
|
||||
<module name="MissingOverride"/>
|
||||
|
||||
</module>
|
||||
<module name="SuppressWarningsFilter"/>
|
||||
</module>
|
||||
|
40
pom.xml
40
pom.xml
@ -20,14 +20,18 @@
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-middleware-picocli</artifactId>
|
||||
<version>0.0-SNAPSHOT</version>
|
||||
<version>0.0.0</version>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git@github.com:Kingsrook/qqq-middleware-picocli.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:Kingsrook/qqq-middleware-picocli.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<!-- props specifically to this module -->
|
||||
@ -47,12 +51,12 @@
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-core</artifactId>
|
||||
<version>0.0-SNAPSHOT</version>
|
||||
<version>0.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-module-rdbms</artifactId>
|
||||
<version>0.0-SNAPSHOT</version>
|
||||
<version>0.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@ -65,7 +69,7 @@
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.197</version>
|
||||
<version>2.1.210</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@ -78,12 +82,12 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.15.0</version>
|
||||
<version>2.17.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.15.0</version>
|
||||
<version>2.17.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
@ -156,12 +160,28 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.amashchenko.maven.plugin</groupId>
|
||||
<artifactId>gitflow-maven-plugin</artifactId>
|
||||
<version>1.18.0</version>
|
||||
<configuration>
|
||||
<gitFlowConfig>
|
||||
<productionBranch>main</productionBranch>
|
||||
<developmentBranch>dev</developmentBranch>
|
||||
<versionTagPrefix>version-</versionTagPrefix>
|
||||
</gitFlowConfig>
|
||||
<skipFeatureVersion>true</skipFeatureVersion> <!-- Keep feature names out of versions -->
|
||||
<postReleaseGoals>install</postReleaseGoals> <!-- Let CI run deploys -->
|
||||
<commitDevelopmentVersionAtStart>true</commitDevelopmentVersionAtStart>
|
||||
<versionDigitToIncrement>1</versionDigitToIncrement> <!-- In general, we update the minor -->
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>github</id>
|
||||
<id>github-qqq-maven-registry</id>
|
||||
<name>GitHub QQQ Maven Registry</name>
|
||||
<url>https://maven.pkg.github.com/Kingsrook/qqq-maven-registry</url>
|
||||
</repository>
|
||||
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.frontend.picocli;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
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 picocli.CommandLine;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define how a PicoCLI process gets data back to a QProcess.
|
||||
*******************************************************************************/
|
||||
public class PicoCliProcessCallback implements QProcessCallback
|
||||
{
|
||||
private final CommandLine commandLine;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor that takes the picocli CommandLine object
|
||||
*******************************************************************************/
|
||||
public PicoCliProcessCallback(CommandLine commandLine)
|
||||
{
|
||||
this.commandLine = commandLine;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Get the filter query for this callback.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QQueryFilter getQueryFilter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Get the field values for this callback.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Map<String, Serializable> getFieldValues(List<QFieldMetaData> fields)
|
||||
{
|
||||
Map<String, Serializable> rs = new HashMap<>();
|
||||
final Scanner scanner = new Scanner(System.in);
|
||||
|
||||
///////////////////////////////////
|
||||
// todo - only if "interactive?" //
|
||||
///////////////////////////////////
|
||||
for(QFieldMetaData field : fields)
|
||||
{
|
||||
commandLine.getOut().println("Please supply a value for the field: [" + field.getLabel() + "]:");
|
||||
rs.put(field.getName(), scanner.nextLine());
|
||||
}
|
||||
|
||||
return (rs);
|
||||
}
|
||||
|
||||
}
|
@ -25,12 +25,16 @@ package com.kingsrook.qqq.frontend.picocli;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import picocli.CommandLine;
|
||||
|
||||
|
||||
@ -94,9 +98,27 @@ public class QCommandBuilder
|
||||
List<QProcessMetaData> processes = qInstance.getProcessesForTable(tableName);
|
||||
if(CollectionUtils.nullSafeHasContents(processes))
|
||||
{
|
||||
tableCommand.addSubcommand("process", defineTableProcessesCommand(table, processes));
|
||||
tableCommand.addSubcommand("process", defineProcessesCommand(processes));
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// add all orphan processes (e.g., ones without tables) to the top-level //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
List<QProcessMetaData> orphanProcesses = new ArrayList<>();
|
||||
for(QProcessMetaData process : qInstance.getProcesses().values())
|
||||
{
|
||||
if(!StringUtils.hasContent(process.getTableName()))
|
||||
{
|
||||
orphanProcesses.add(process);
|
||||
}
|
||||
}
|
||||
|
||||
if(!orphanProcesses.isEmpty())
|
||||
{
|
||||
topCommandSpec.addSubcommand("processes", defineProcessesCommand(orphanProcesses));
|
||||
}
|
||||
|
||||
return topCommandSpec;
|
||||
}
|
||||
|
||||
@ -233,14 +255,34 @@ public class QCommandBuilder
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private CommandLine.Model.CommandSpec defineTableProcessesCommand(QTableMetaData table, List<QProcessMetaData> processes)
|
||||
private CommandLine.Model.CommandSpec defineProcessesCommand(List<QProcessMetaData> processes)
|
||||
{
|
||||
CommandLine.Model.CommandSpec processesCommand = CommandLine.Model.CommandSpec.create();
|
||||
|
||||
for(QProcessMetaData process : processes)
|
||||
{
|
||||
///////////////////////////////////////////
|
||||
// add the sub-command to run the proces //
|
||||
///////////////////////////////////////////
|
||||
CommandLine.Model.CommandSpec processCommand = CommandLine.Model.CommandSpec.create();
|
||||
processesCommand.addSubcommand(process.getName(), processCommand);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// add all (distinct, by name) input fields to the command as --field-* options //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
Map<String, QFieldMetaData> inputFieldMap = new LinkedHashMap<>();
|
||||
for(QFieldMetaData inputField : process.getInputFields())
|
||||
{
|
||||
inputFieldMap.put(inputField.getName(), inputField);
|
||||
}
|
||||
|
||||
for(QFieldMetaData field : inputFieldMap.values())
|
||||
{
|
||||
processCommand.addOption(CommandLine.Model.OptionSpec.builder("--field-" + field.getName())
|
||||
.type(getClassForField(field))
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (processesCommand);
|
||||
|
@ -36,7 +36,7 @@ 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.RunFunctionAction;
|
||||
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.CsvToQRecordAdapter;
|
||||
@ -53,6 +53,8 @@ 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.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter;
|
||||
@ -62,6 +64,7 @@ import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.AbstractQFiel
|
||||
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.metadata.processes.QProcessMetaData;
|
||||
@ -247,9 +250,23 @@ public class QPicoCliImplementation
|
||||
}
|
||||
else
|
||||
{
|
||||
String subCommandName = parseResult.subcommand().commandSpec().name();
|
||||
ParseResult subParseResult = parseResult.subcommand();
|
||||
String subCommandName = subParseResult.commandSpec().name();
|
||||
CommandLine subCommandLine = commandLine.getSubcommands().get(subCommandName);
|
||||
return runTableLevelCommand(subCommandLine, parseResult.subcommand());
|
||||
switch(subCommandName)
|
||||
{
|
||||
case "processes":
|
||||
{
|
||||
return runProcessCommand(subCommandLine, subParseResult);
|
||||
}
|
||||
default:
|
||||
{
|
||||
/////////////////////////////////////////////////////////
|
||||
// by default, assume the command here is a table name //
|
||||
/////////////////////////////////////////////////////////
|
||||
return runTableLevelCommand(subCommandLine, subParseResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,7 +308,7 @@ public class QPicoCliImplementation
|
||||
case "process":
|
||||
{
|
||||
CommandLine subCommandLine = commandLine.getSubcommands().get(subCommandName);
|
||||
return runTableProcess(subCommandLine, tableName, subParseResult);
|
||||
return runProcessCommand(subCommandLine, subParseResult);
|
||||
}
|
||||
default:
|
||||
{
|
||||
@ -311,35 +328,74 @@ public class QPicoCliImplementation
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Handle a command up to the point where 'process' was given
|
||||
*******************************************************************************/
|
||||
private int runTableProcess(CommandLine commandLine, String tableName, ParseResult subParseResult)
|
||||
private int runProcessCommand(CommandLine commandLine, ParseResult subParseResult)
|
||||
{
|
||||
if(!subParseResult.hasSubcommand())
|
||||
{
|
||||
////////////////////////////////////////////////////////////////
|
||||
// process name must be a sub-command, so, error if not given //
|
||||
////////////////////////////////////////////////////////////////
|
||||
commandLine.usage(commandLine.getOut());
|
||||
return commandLine.getCommandSpec().exitCodeOnUsageHelp();
|
||||
}
|
||||
else
|
||||
{
|
||||
///////////////////////////////////////////
|
||||
// move on to running the actual process //
|
||||
///////////////////////////////////////////
|
||||
String subCommandName = subParseResult.subcommand().commandSpec().name();
|
||||
CommandLine subCommandLine = commandLine.getSubcommands().get(subCommandName);
|
||||
return runTableProcessLevelCommand(subCommandLine, tableName, subParseResult.subcommand());
|
||||
return runActualProcess(subCommandLine, subParseResult.subcommand());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** actually run a process (the process name should be at the start of the sub-command line)
|
||||
*******************************************************************************/
|
||||
private int runTableProcessLevelCommand(CommandLine subCommandLine, String tableName, ParseResult processParseResult)
|
||||
private int runActualProcess(CommandLine subCommandLine, ParseResult processParseResult)
|
||||
{
|
||||
String processName = processParseResult.commandSpec().name();
|
||||
QTableMetaData table = qInstance.getTable(tableName);
|
||||
QProcessMetaData process = qInstance.getProcess(processName);
|
||||
RunFunctionAction runFunctionAction = new RunFunctionAction();
|
||||
// todo!
|
||||
RunProcessRequest request = new RunProcessRequest(qInstance);
|
||||
|
||||
request.setSession(session);
|
||||
request.setProcessName(processName);
|
||||
request.setCallback(new PicoCliProcessCallback(subCommandLine));
|
||||
|
||||
for(OptionSpec matchedOption : processParseResult.matchedOptions())
|
||||
{
|
||||
if(matchedOption.longestName().startsWith("--field-"))
|
||||
{
|
||||
String fieldName = matchedOption.longestName().substring(8);
|
||||
request.addValue(fieldName, matchedOption.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
RunProcessResult result = new RunProcessAction().execute(request);
|
||||
subCommandLine.getOut().println("Process Results: "); // todo better!!
|
||||
for(QFieldMetaData outputField : process.getOutputFields())
|
||||
{
|
||||
subCommandLine.getOut().format(" %s: %s\n", outputField.getLabel(), result.getValues().get(outputField.getName()));
|
||||
}
|
||||
|
||||
if(result.getError() != null)
|
||||
{
|
||||
subCommandLine.getOut().println("Process Error message: " + result.getError());
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
subCommandLine.getOut().println("Caught Exception running process. See stack trace above for details.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -22,10 +22,12 @@
|
||||
package com.kingsrook.qqq.frontend.picocli;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -37,7 +39,6 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@ -133,12 +134,20 @@ class QPicoCliImplementationTest
|
||||
TestOutput testOutput = testCli("--meta-data");
|
||||
JSONObject metaData = JsonUtils.toJSONObject(testOutput.getOutput());
|
||||
assertNotNull(metaData);
|
||||
assertEquals(1, metaData.keySet().size(), "Number of top-level keys");
|
||||
assertEquals(2, metaData.keySet().size(), "Number of top-level keys");
|
||||
|
||||
assertTrue(metaData.has("tables"));
|
||||
JSONObject tables = metaData.getJSONObject("tables");
|
||||
JSONObject personTable = tables.getJSONObject("person");
|
||||
assertEquals("person", personTable.getString("name"));
|
||||
assertEquals("Person", personTable.getString("label"));
|
||||
|
||||
assertTrue(metaData.has("processes"));
|
||||
JSONObject processes = metaData.getJSONObject("processes");
|
||||
JSONObject greetProcess = processes.getJSONObject("greet");
|
||||
assertEquals("greet", greetProcess.getString("name"));
|
||||
assertEquals("Greet", greetProcess.getString("label"));
|
||||
assertEquals("person", greetProcess.getString("tableName"));
|
||||
}
|
||||
|
||||
|
||||
@ -246,7 +255,8 @@ class QPicoCliImplementationTest
|
||||
{
|
||||
TestOutput testOutput = testCli("person", "insert",
|
||||
"--field-firstName=Lucy",
|
||||
"--field-lastName=Lu");
|
||||
"--field-lastName=Lu",
|
||||
"--field-email=lucy@kingsrook.com");
|
||||
JSONObject insertResult = JsonUtils.toJSONObject(testOutput.getOutput());
|
||||
assertNotNull(insertResult);
|
||||
assertEquals(1, insertResult.getJSONArray("records").length());
|
||||
@ -263,11 +273,11 @@ class QPicoCliImplementationTest
|
||||
public void test_tableInsertJsonObjectArgumentWithMapping()
|
||||
{
|
||||
String mapping = """
|
||||
--mapping={"firstName":"first","lastName":"ln"}
|
||||
--mapping={"firstName":"first","lastName":"ln","email":"email"}
|
||||
""";
|
||||
|
||||
String jsonBody = """
|
||||
--jsonBody={"first":"Chester","ln":"Cheese"}
|
||||
--jsonBody={"first":"Chester","ln":"Cheese","email":"chester@kingsrook.com"}
|
||||
""";
|
||||
|
||||
TestOutput testOutput = testCli("person", "insert", mapping, jsonBody);
|
||||
@ -277,6 +287,7 @@ class QPicoCliImplementationTest
|
||||
assertEquals(6, insertResult.getJSONArray("records").getJSONObject(0).getJSONObject("values").getInt("id"));
|
||||
assertEquals("Chester", insertResult.getJSONArray("records").getJSONObject(0).getJSONObject("values").getString("firstName"));
|
||||
assertEquals("Cheese", insertResult.getJSONArray("records").getJSONObject(0).getJSONObject("values").getString("lastName"));
|
||||
assertEquals("chester@kingsrook.com", insertResult.getJSONArray("records").getJSONObject(0).getJSONObject("values").getString("email"));
|
||||
}
|
||||
|
||||
|
||||
@ -289,11 +300,14 @@ class QPicoCliImplementationTest
|
||||
public void test_tableInsertJsonArrayFileWithMapping() throws IOException
|
||||
{
|
||||
String mapping = """
|
||||
--mapping={"firstName":"first","lastName":"ln"}
|
||||
--mapping={"firstName":"first","lastName":"ln","email":"email"}
|
||||
""";
|
||||
|
||||
String jsonContents = """
|
||||
[{"first":"Charlie","ln":"Bear"},{"first":"Coco","ln":"Bean"}]
|
||||
[
|
||||
{"first":"Charlie","ln":"Bear","email":"charlie-bear@kingsrook.com"},
|
||||
{"first":"Coco","ln":"Bean","email":"coco-bean@kingsrook.com"}
|
||||
]
|
||||
""";
|
||||
|
||||
File file = new File("/tmp/" + UUID.randomUUID() + ".json");
|
||||
@ -309,8 +323,10 @@ class QPicoCliImplementationTest
|
||||
assertEquals(7, insertResult.getJSONArray("records").getJSONObject(1).getJSONObject("values").getInt("id"));
|
||||
assertEquals("Charlie", records.getJSONObject(0).getJSONObject("values").getString("firstName"));
|
||||
assertEquals("Bear", records.getJSONObject(0).getJSONObject("values").getString("lastName"));
|
||||
assertEquals("charlie-bear@kingsrook.com", records.getJSONObject(0).getJSONObject("values").getString("email"));
|
||||
assertEquals("Coco", records.getJSONObject(1).getJSONObject("values").getString("firstName"));
|
||||
assertEquals("Bean", records.getJSONObject(1).getJSONObject("values").getString("lastName"));
|
||||
assertEquals("coco-bean@kingsrook.com", records.getJSONObject(1).getJSONObject("values").getString("email"));
|
||||
}
|
||||
|
||||
|
||||
@ -323,12 +339,12 @@ class QPicoCliImplementationTest
|
||||
public void test_tableInsertCsvFileWithIndexMapping() throws IOException
|
||||
{
|
||||
String mapping = """
|
||||
--mapping={"firstName":1,"lastName":3}
|
||||
--mapping={"firstName":1,"lastName":3,"email":5}
|
||||
""";
|
||||
|
||||
String csvContents = """
|
||||
"Louis","P","Willikers",1024,
|
||||
"Nestle","G","Crunch",1701,
|
||||
"Louis","P","Willikers",1024,"louis@kingsrook.com",
|
||||
"Nestle","G","Crunch",1701,"nestle@kingsrook.com",
|
||||
|
||||
""";
|
||||
|
||||
@ -465,12 +481,25 @@ class QPicoCliImplementationTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
@Disabled // not yet done.
|
||||
public void test_tableProcessGreet() throws Exception
|
||||
public void test_tableProcessGreetUsingCallbackForFields() throws Exception
|
||||
{
|
||||
setStandardInputLines("Hi", "How are you?");
|
||||
TestOutput testOutput = testCli("person", "process", "greet");
|
||||
assertTestOutputContains(testOutput, "Please supply a value for the field.*Greeting Prefix");
|
||||
assertTestOutputContains(testOutput, "Hi X How are you?");
|
||||
}
|
||||
|
||||
fail("Assertion not written...");
|
||||
|
||||
/*******************************************************************************
|
||||
** test running a process on a table
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_tableProcessGreetUsingOptionsForFields() throws Exception
|
||||
{
|
||||
TestOutput testOutput = testCli("person", "process", "greet", "--field-greetingPrefix=Hello", "--field-greetingSuffix=There");
|
||||
assertTestOutputDoesNotContain(testOutput, "Please supply a value for the field");
|
||||
assertTestOutputContains(testOutput, "Hello X There");
|
||||
}
|
||||
|
||||
|
||||
@ -560,6 +589,26 @@ class QPicoCliImplementationTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void setStandardInputLines(String... lines)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for(String line : lines)
|
||||
{
|
||||
stringBuilder.append(line);
|
||||
if(!line.endsWith("\n"))
|
||||
{
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
}
|
||||
ByteArrayInputStream stdin = new ByteArrayInputStream(stringBuilder.toString().getBytes(Charset.defaultCharset()));
|
||||
System.setIn(stdin);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -25,8 +25,8 @@ package com.kingsrook.qqq.frontend.picocli;
|
||||
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.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;
|
||||
@ -41,7 +41,7 @@ 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.RDBMSBackendMetaData;
|
||||
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;
|
||||
@ -63,10 +63,11 @@ public class TestUtils
|
||||
public static void primeTestDatabase() throws Exception
|
||||
{
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(TestUtils.defineBackend()));
|
||||
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
|
||||
InputStream primeTestDatabaseSqlStream = TestUtils.class.getResourceAsStream("/prime-test-database.sql");
|
||||
assertNotNull(primeTestDatabaseSqlStream);
|
||||
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
|
||||
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
|
||||
String joinedSQL = String.join("\n", lines);
|
||||
for(String sql : joinedSQL.split(";"))
|
||||
{
|
||||
@ -83,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);
|
||||
}
|
||||
|
||||
@ -122,16 +123,15 @@ 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", "");
|
||||
return (new RDBMSBackendMetaData()
|
||||
.withVendor("h2")
|
||||
.withHostName("mem")
|
||||
.withDatabaseName("test_database")
|
||||
.withUsername("sa")
|
||||
.withPassword("")
|
||||
.withName("default"));
|
||||
}
|
||||
|
||||
|
||||
@ -169,7 +169,7 @@ public class TestUtils
|
||||
.addFunction(new QFunctionMetaData()
|
||||
.withName("prepare")
|
||||
.withCode(new QCodeReference()
|
||||
.withName("com.kingsrook.qqq.backend.core.interfaces.mock.MockFunctionBody")
|
||||
.withName(MockFunctionBody.class.getName())
|
||||
.withCodeType(QCodeType.JAVA)
|
||||
.withCodeUsage(QCodeUsage.FUNCTION)) // todo - needed, or implied in this context?
|
||||
.withInputData(new QFunctionInputMetaData()
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
--
|
||||
-- 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/>.
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS person;
|
||||
CREATE TABLE person
|
||||
(
|
||||
id SERIAL,
|
||||
id INT AUTO_INCREMENT,
|
||||
create_date TIMESTAMP DEFAULT now(),
|
||||
modify_date TIMESTAMP DEFAULT now(),
|
||||
|
||||
|
Reference in New Issue
Block a user