Merge branch 'release/0.1.0'

This commit is contained in:
2022-07-14 10:14:44 -05:00
7 changed files with 176 additions and 40 deletions

View File

@ -42,7 +42,7 @@ jobs:
executor: java17 executor: java17
steps: steps:
- run_maven: - run_maven:
maven_subcommand: test maven_subcommand: verify
- slack/notify: - slack/notify:
event: fail event: fail

View File

@ -181,8 +181,8 @@
</module> </module>
--> -->
<module name="OverloadMethodsDeclarationOrder"/> <module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<!-- <!--
<module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder"> <module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/> <property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/> <property name="separateLineBetweenGroups" value="true"/>

89
pom.xml
View File

@ -25,7 +25,7 @@
<groupId>com.kingsrook.qqq</groupId> <groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-middleware-picocli</artifactId> <artifactId>qqq-middleware-picocli</artifactId>
<version>0.0.0</version> <version>0.1.0</version>
<scm> <scm>
<connection>scm:git:git@github.com:Kingsrook/qqq-middleware-picocli.git</connection> <connection>scm:git:git@github.com:Kingsrook/qqq-middleware-picocli.git</connection>
@ -44,6 +44,8 @@
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation> <maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
<maven.compiler.showWarnings>true</maven.compiler.showWarnings> <maven.compiler.showWarnings>true</maven.compiler.showWarnings>
<coverage.haltOnFailure>true</coverage.haltOnFailure>
<coverage.instructionCoveredRatioMinimum>0.80</coverage.instructionCoveredRatioMinimum>
</properties> </properties>
<dependencies> <dependencies>
@ -51,12 +53,12 @@
<dependency> <dependency>
<groupId>com.kingsrook.qqq</groupId> <groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-backend-core</artifactId> <artifactId>qqq-backend-core</artifactId>
<version>0.0.0</version> <version>0.1.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.kingsrook.qqq</groupId> <groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-backend-module-rdbms</artifactId> <artifactId>qqq-backend-module-rdbms</artifactId>
<version>0.0.0</version> <version>0.1.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
@ -127,6 +129,9 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version> <version>3.0.0-M5</version>
<configuration>
<argLine>@{jaCoCoArgLine}</argLine>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@ -176,6 +181,84 @@
<versionDigitToIncrement>1</versionDigitToIncrement> <!-- In general, we update the minor --> <versionDigitToIncrement>1</versionDigitToIncrement> <!-- In general, we update the minor -->
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>jaCoCoArgLine</propertyName>
</configuration>
</execution>
<execution>
<id>unit-test-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<!-- Gives us the ability to pass a parameter to not fail due to coverage E.g. -Dcoverage.haltOnFailure=false -->
<haltOnFailure>${coverage.haltOnFailure}</haltOnFailure>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>INSTRUCTION</counter>
<value>COVEREDRATIO</value>
<minimum>${coverage.instructionCoveredRatioMinimum}</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
<execution>
<id>post-unit-test</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>exec-maven-plugin</artifactId>
<groupId>org.codehaus.mojo</groupId>
<version>3.0.0</version>
<executions>
<execution>
<id>test-coverage-summary</id>
<phase>verify</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>sh</executable>
<arguments>
<argument>-c</argument>
<argument>
<![CDATA[
echo "Element\nInstructions Missed\nInstruction Coverage\nBranches Missed\nBranch Coverage\nComplexity Missed\nComplexity Hit\nLines Missed\nLines Hit\nMethods Missed\nMethods Hit\nClasses Missed\nClasses Hit\n" > /tmp/$$.headers
xpath -q -e '/html/body/table/tfoot/tr[1]/td/text()' target/site/jacoco/index.html > /tmp/$$.values
echo
echo "Jacoco coverage summary report:"
echo " See also target/site/jacoco/index.html"
echo " and https://www.jacoco.org/jacoco/trunk/doc/counters.html"
echo "------------------------------------------------------------"
paste /tmp/$$.headers /tmp/$$.values | tail +2 | awk -v FS='\t' '{printf("%-20s %s\n",$1,$2)}'
rm /tmp/$$.headers /tmp/$$.values
]]>
</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -90,6 +90,7 @@ public class QCommandBuilder
// add table-specific sub-commands for the table // // add table-specific sub-commands for the table //
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
tableCommand.addSubcommand("meta-data", defineMetaDataCommand(table)); tableCommand.addSubcommand("meta-data", defineMetaDataCommand(table));
tableCommand.addSubcommand("count", defineQueryCommand(table));
tableCommand.addSubcommand("query", defineQueryCommand(table)); tableCommand.addSubcommand("query", defineQueryCommand(table));
tableCommand.addSubcommand("insert", defineInsertCommand(table)); tableCommand.addSubcommand("insert", defineInsertCommand(table));
tableCommand.addSubcommand("update", defineUpdateCommand(table)); tableCommand.addSubcommand("update", defineUpdateCommand(table));

View File

@ -32,6 +32,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.CountAction;
import com.kingsrook.qqq.backend.core.actions.DeleteAction; import com.kingsrook.qqq.backend.core.actions.DeleteAction;
import com.kingsrook.qqq.backend.core.actions.InsertAction; import com.kingsrook.qqq.backend.core.actions.InsertAction;
import com.kingsrook.qqq.backend.core.actions.MetaDataAction; import com.kingsrook.qqq.backend.core.actions.MetaDataAction;
@ -45,6 +46,8 @@ import com.kingsrook.qqq.backend.core.adapters.JsonToQRecordAdapter;
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter; import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException; import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
import com.kingsrook.qqq.backend.core.model.actions.count.CountRequest;
import com.kingsrook.qqq.backend.core.model.actions.count.CountResult;
import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteRequest; import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteRequest;
import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteResult; import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteResult;
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest; import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest;
@ -289,6 +292,10 @@ public class QPicoCliImplementation
{ {
return runTableMetaData(commandLine, tableName, subParseResult); return runTableMetaData(commandLine, tableName, subParseResult);
} }
case "count":
{
return runTableCount(commandLine, tableName, subParseResult);
}
case "query": case "query":
{ {
return runTableQuery(commandLine, tableName, subParseResult); return runTableQuery(commandLine, tableName, subParseResult);
@ -384,9 +391,10 @@ public class QPicoCliImplementation
subCommandLine.getOut().format(" %s: %s\n", outputField.getLabel(), result.getValues().get(outputField.getName())); subCommandLine.getOut().format(" %s: %s\n", outputField.getLabel(), result.getValues().get(outputField.getName()));
} }
if(result.getError() != null) if(result.getException().isPresent())
{ {
subCommandLine.getOut().println("Process Error message: " + result.getError()); // todo - user-facing, similar to javalin
subCommandLine.getOut().println("Process Error message: " + result.getException().get().getMessage());
} }
} }
catch(Exception e) catch(Exception e)
@ -417,6 +425,24 @@ public class QPicoCliImplementation
/*******************************************************************************
**
*******************************************************************************/
private int runTableCount(CommandLine commandLine, String tableName, ParseResult subParseResult) throws QException
{
CountRequest countRequest = new CountRequest(qInstance);
countRequest.setSession(session);
countRequest.setTableName(tableName);
countRequest.setFilter(generateQueryFilter(subParseResult));
CountAction countAction = new CountAction();
CountResult countResult = countAction.execute(countRequest);
commandLine.getOut().println(JsonUtils.toPrettyJson(countResult));
return commandLine.getCommandSpec().exitCodeOnSuccess();
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -427,9 +453,22 @@ public class QPicoCliImplementation
queryRequest.setTableName(tableName); queryRequest.setTableName(tableName);
queryRequest.setSkip(subParseResult.matchedOptionValue("skip", null)); queryRequest.setSkip(subParseResult.matchedOptionValue("skip", null));
queryRequest.setLimit(subParseResult.matchedOptionValue("limit", DEFAULT_LIMIT)); queryRequest.setLimit(subParseResult.matchedOptionValue("limit", DEFAULT_LIMIT));
queryRequest.setFilter(generateQueryFilter(subParseResult));
QueryAction queryAction = new QueryAction();
QueryResult queryResult = queryAction.execute(queryRequest);
commandLine.getOut().println(JsonUtils.toPrettyJson(queryResult));
return commandLine.getCommandSpec().exitCodeOnSuccess();
}
/*******************************************************************************
**
*******************************************************************************/
private QQueryFilter generateQueryFilter(ParseResult subParseResult)
{
QQueryFilter filter = new QQueryFilter(); QQueryFilter filter = new QQueryFilter();
queryRequest.setFilter(filter);
String[] criteria = subParseResult.matchedOptionValue("criteria", new String[] {}); String[] criteria = subParseResult.matchedOptionValue("criteria", new String[] {});
for(String criterion : criteria) for(String criterion : criteria)
@ -443,10 +482,7 @@ public class QPicoCliImplementation
filter.addCriteria(qQueryCriteria); filter.addCriteria(qQueryCriteria);
} }
QueryAction queryAction = new QueryAction(); return filter;
QueryResult queryResult = queryAction.execute(queryRequest);
commandLine.getOut().println(JsonUtils.toPrettyJson(queryResult));
return commandLine.getCommandSpec().exitCodeOnSuccess();
} }

View File

@ -202,7 +202,6 @@ class QPicoCliImplementationTest
assertNotNull(metaData); assertNotNull(metaData);
assertEquals(1, metaData.keySet().size(), "Number of top-level keys"); assertEquals(1, metaData.keySet().size(), "Number of top-level keys");
JSONObject table = metaData.getJSONObject("table"); JSONObject table = metaData.getJSONObject("table");
assertEquals(4, table.keySet().size(), "Number of mid-level keys");
assertEquals("person", table.getString("name")); assertEquals("person", table.getString("name"));
assertEquals("Person", table.getString("label")); assertEquals("Person", table.getString("label"));
assertEquals("id", table.getString("primaryKeyField")); assertEquals("id", table.getString("primaryKeyField"));
@ -214,6 +213,28 @@ class QPicoCliImplementationTest
/*******************************************************************************
** test running a count on a table
**
*******************************************************************************/
@Test
public void test_tableCount()
{
TestOutput testOutput = testCli("person", "count", "--criteria", "id NOT_EQUALS 3");
JSONObject countResult = JsonUtils.toJSONObject(testOutput.getOutput());
assertNotNull(countResult);
int count = countResult.getInt("count");
assertEquals(4, count);
testOutput = testCli("person", "count", "--criteria", "id EQUALS 3");
countResult = JsonUtils.toJSONObject(testOutput.getOutput());
assertNotNull(countResult);
count = countResult.getInt("count");
assertEquals(1, count);
}
/******************************************************************************* /*******************************************************************************
** test running a query on a table ** test running a query on a table
** **
@ -497,9 +518,9 @@ class QPicoCliImplementationTest
@Test @Test
public void test_tableProcessGreetUsingOptionsForFields() throws Exception public void test_tableProcessGreetUsingOptionsForFields() throws Exception
{ {
TestOutput testOutput = testCli("person", "process", "greet", "--field-greetingPrefix=Hello", "--field-greetingSuffix=There"); TestOutput testOutput = testCli("person", "process", "greet", "--field-greetingPrefix=Hello", "--field-greetingSuffix=World");
assertTestOutputDoesNotContain(testOutput, "Please supply a value for the field"); assertTestOutputDoesNotContain(testOutput, "Please supply a value for the field");
assertTestOutputContains(testOutput, "Hello X There"); assertTestOutputContains(testOutput, "Hello X World");
} }

View File

@ -25,7 +25,7 @@ package com.kingsrook.qqq.frontend.picocli;
import java.io.InputStream; import java.io.InputStream;
import java.sql.Connection; import java.sql.Connection;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.interfaces.mock.MockFunctionBody; import com.kingsrook.qqq.backend.core.interfaces.mock.MockBackendStep;
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference; 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.QCodeType;
@ -34,16 +34,14 @@ 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.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData; 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.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.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData; 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.ConnectionManager;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNotNull;
@ -166,12 +164,12 @@ public class TestUtils
return new QProcessMetaData() return new QProcessMetaData()
.withName("greet") .withName("greet")
.withTableName("person") .withTableName("person")
.addFunction(new QFunctionMetaData() .addStep(new QBackendStepMetaData()
.withName("prepare") .withName("prepare")
.withCode(new QCodeReference() .withCode(new QCodeReference()
.withName(MockFunctionBody.class.getName()) .withName(MockBackendStep.class.getName())
.withCodeType(QCodeType.JAVA) .withCodeType(QCodeType.JAVA)
.withCodeUsage(QCodeUsage.FUNCTION)) // todo - needed, or implied in this context? .withCodeUsage(QCodeUsage.BACKEND_STEP)) // todo - needed, or implied in this context?
.withInputData(new QFunctionInputMetaData() .withInputData(new QFunctionInputMetaData()
.withRecordListMetaData(new QRecordListMetaData().withTableName("person")) .withRecordListMetaData(new QRecordListMetaData().withTableName("person"))
.withFieldList(List.of( .withFieldList(List.of(
@ -184,9 +182,6 @@ public class TestUtils
.addField(new QFieldMetaData("fullGreeting", QFieldType.STRING)) .addField(new QFieldMetaData("fullGreeting", QFieldType.STRING))
) )
.withFieldList(List.of(new QFieldMetaData("outputMessage", 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"))))
); );
} }