Merged dev into feature/meta-data-loaders

This commit is contained in:
2025-01-17 19:12:48 -06:00
14 changed files with 159 additions and 135 deletions

View File

@ -127,14 +127,9 @@ commands:
command: | command: |
cd docs cd docs
asciidoctor -a docinfo=shared index.adoc asciidoctor -a docinfo=shared index.adoc
- store_artifacts:
upload_docs_site: path: docs/index.html
steps: when: always
- run:
name: scp html to justinsgotskinnylegs.com
command: |
cd docs
scp index.html dkelkhoff@45.79.44.221:/mnt/first-volume/dkelkhoff/nginx/html/justinsgotskinnylegs.com/qqq-docs.html
jobs: jobs:
mvn_test: mvn_test:
@ -159,7 +154,6 @@ jobs:
steps: steps:
- install_asciidoctor - install_asciidoctor
- run_asciidoctor - run_asciidoctor
- upload_docs_site
workflows: workflows:
test_only: test_only:

View File

@ -147,8 +147,8 @@ public class BackendMetaDataProvider
As the size of your application grows, if you're doing per-object meta-data providers, you may find it As the size of your application grows, if you're doing per-object meta-data providers, you may find it
burdensome, when adding a new object to your instance, to have to write code for it in two places - burdensome, when adding a new object to your instance, to have to write code for it in two places -
that is - a new class to produce that meta-data object, AND a single line of code to add that object that is - a new class to produce that meta-data object, AND a single line of code to add that object
to your `QInstance`. As such, a mechanism to avoid that line-of-code to add the object to the to your `QInstance`. As such, a mechanism exists to let you avoid that line-of-code for adding the object
`QInstance` exists. to the `QInstance`.
This mechanism involves adding the `MetaDataProducerInterface` to all of your classes that produce a This mechanism involves adding the `MetaDataProducerInterface` to all of your classes that produce a
meta-data object. This interface is generic, with a type parameter that will typically be the type of meta-data object. This interface is generic, with a type parameter that will typically be the type of
@ -204,7 +204,7 @@ that are all related, and it's only a handful of lines of code for each one, may
produce them all in the same class. Or maybe when you define a table, you'd like to define its produce them all in the same class. Or maybe when you define a table, you'd like to define its
joins and widgets at the same time. joins and widgets at the same time.
This approach can be accomplished by making your `MetaDataProducerInterface`'s type argument by This approach can be accomplished by making the type argument for your `MetaDataProducerInterface` be
`MetaDataProducerMultiOutput` - a simple class that just wraps a list of other `MetaDataProducerOutput` `MetaDataProducerMultiOutput` - a simple class that just wraps a list of other `MetaDataProducerOutput`
objects. objects.
@ -238,12 +238,13 @@ be specified in their meta-data object.
At the same time, if you're writing any custom code in your QQQ application At the same time, if you're writing any custom code in your QQQ application
(e.g., any processes or table customizers), where you're working with records from tables, you may (e.g., any processes or table customizers), where you're working with records from tables, you may
prefer being able to work with entity beans (e.g., java classes with typed getter & setter methods), prefer being able to work with entity beans (e.g., java classes with typed getter & setter methods),
rather than the default object type that QQQ's ORM actions return, the `QRecord`, which carriers all rather than the default object type that QQQ's ORM actions return, the `QRecord`, which carries all
of its values in a `Map`. QQQ has a mechanism for dealing with this - in the form of the `QRecordEntity` of its values in a `Map` (where you don't get compile-time checks of field names or data types).
class. QQQ has a mechanism for dealing with this - in the form of the `QRecordEntity` class.
So - if you want to build your application using entity beans (which is recommended, for the compile-time So - if you want to build your application using entity beans (which is recommended, for the compile-time
safety that they provide in custom code), you will be writing a `QRecordEntity` class, which will look like: safety that they provide in custom code), you will be writing a `QRecordEntity` class for each of your tables,
which will look like:
[source,java] [source,java]
.QRecordEntity example .QRecordEntity example
@ -280,7 +281,7 @@ public QTableMetaData produce(QInstance qInstance) throws QExcpetion
---- ----
That `withFieldsFromEntity` call is one of the biggest benefits of this technique. It allows you to avoid defining That `withFieldsFromEntity` call is one of the biggest benefits of this technique. It allows you to avoid defining
all of the field in you table in two place (the entity and the table meta-data). all of the fields in you table in two places (the entity and the table meta-data).
== MetaData Producing Annotations for Entities == MetaData Producing Annotations for Entities

View File

@ -186,7 +186,7 @@ public class AsyncRecordPipeLoop
if(recordCount > 0) if(recordCount > 0)
{ {
LOG.info("End of job summary", logPair("recordCount", recordCount), logPair("jobName", jobName), logPair("millis", endTime - jobStartTime), logPair("recordsPerSecond", 1000d * (recordCount / (.001d + (endTime - jobStartTime))))); LOG.debug("End of job summary", logPair("recordCount", recordCount), logPair("jobName", jobName), logPair("millis", endTime - jobStartTime), logPair("recordsPerSecond", 1000d * (recordCount / (.001d + (endTime - jobStartTime)))));
} }
return (recordCount); return (recordCount);

View File

@ -120,6 +120,16 @@ public enum QFieldType
/*******************************************************************************
**
*******************************************************************************/
public boolean isTemporal()
{
return this == QFieldType.DATE || this == QFieldType.DATE_TIME || this == QFieldType.TIME;
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -189,7 +189,7 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
if(recordCount > 0) if(recordCount > 0)
{ {
LOG.info("Processed [" + recordCount + "] records."); LOG.debug("Processed [" + recordCount + "] records.");
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////

View File

@ -223,7 +223,7 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
{ {
if(CollectionUtils.nullSafeIsEmpty(runBackendStepInput.getRecords())) if(CollectionUtils.nullSafeIsEmpty(runBackendStepInput.getRecords()))
{ {
LOG.info("No input records were found."); LOG.debug("No input records were found.");
return; return;
} }

View File

@ -159,6 +159,10 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
{ {
fieldType = QFieldType.DECIMAL; fieldType = QFieldType.DECIMAL;
} }
else if(field.getType().isTemporal() && (aggregate.getOperator().equals(AggregateOperator.COUNT)) || aggregate.getOperator().equals(AggregateOperator.COUNT_DISTINCT))
{
fieldType = QFieldType.INTEGER;
}
} }
if(fieldType != null) if(fieldType != null)

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
@ -42,13 +43,16 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
@ -83,20 +87,56 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
Aggregate averageOfDaysWorked = new Aggregate("daysWorked", AggregateOperator.AVG); Aggregate averageOfDaysWorked = new Aggregate("daysWorked", AggregateOperator.AVG);
Aggregate maxAnnualSalary = new Aggregate("annualSalary", AggregateOperator.MAX); Aggregate maxAnnualSalary = new Aggregate("annualSalary", AggregateOperator.MAX);
Aggregate minFirstName = new Aggregate("firstName", AggregateOperator.MIN); Aggregate minFirstName = new Aggregate("firstName", AggregateOperator.MIN);
Aggregate countOfBirthDate = new Aggregate("birthDate", AggregateOperator.COUNT);
aggregateInput.withAggregate(countOfId); aggregateInput.withAggregate(countOfId);
aggregateInput.withAggregate(sumOfId); aggregateInput.withAggregate(sumOfId);
aggregateInput.withAggregate(averageOfDaysWorked); aggregateInput.withAggregate(averageOfDaysWorked);
aggregateInput.withAggregate(maxAnnualSalary); aggregateInput.withAggregate(maxAnnualSalary);
aggregateInput.withAggregate(minFirstName); aggregateInput.withAggregate(minFirstName);
aggregateInput.withAggregate(countOfBirthDate);
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
AggregateResult aggregateResult = aggregateOutput.getResults().get(0); AggregateResult aggregateResult = aggregateOutput.getResults().get(0);
Assertions.assertEquals(5, aggregateResult.getAggregateValue(countOfId)); assertEquals(5, aggregateResult.getAggregateValue(countOfId));
Assertions.assertEquals(15, aggregateResult.getAggregateValue(sumOfId)); assertEquals(15, aggregateResult.getAggregateValue(sumOfId));
Assertions.assertEquals(new BigDecimal("96.4"), aggregateResult.getAggregateValue(averageOfDaysWorked)); assertEquals(new BigDecimal("96.4"), aggregateResult.getAggregateValue(averageOfDaysWorked));
Assertions.assertEquals(new BigDecimal("1000000.00"), aggregateResult.getAggregateValue(maxAnnualSalary)); assertEquals(new BigDecimal("1000000.00"), aggregateResult.getAggregateValue(maxAnnualSalary));
Assertions.assertEquals("Darin", aggregateResult.getAggregateValue(minFirstName)); assertEquals("Darin", aggregateResult.getAggregateValue(minFirstName));
assertEquals(4, aggregateResult.getAggregateValue(countOfBirthDate));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
@Disabled("Interesting to see effects of all operators on all types, but failures are expected (e.g., avg(string), so not for CI.")
void testOperatorsCrossTypes()
{
List<String> failures = new ArrayList<>();
for(QFieldMetaData field : QContext.getQInstance().getTable(TestUtils.TABLE_NAME_PERSON).getFields().values())
{
for(AggregateOperator aggregateOperator : AggregateOperator.values())
{
try
{
AggregateInput aggregateInput = initAggregateRequest();
Aggregate aggregate = new Aggregate(field.getName(), aggregateOperator);
aggregateInput.withAggregate(aggregate);
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
AggregateResult aggregateResult = aggregateOutput.getResults().get(0);
assertNotNull(aggregateResult.getAggregateValue(aggregate));
}
catch(Exception e)
{
failures.add(ExceptionUtils.getRootException(e).getMessage());
}
}
}
failures.forEach(System.out::println);
} }
@ -123,11 +163,11 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
AggregateResult aggregateResult = aggregateOutput.getResults().get(0); AggregateResult aggregateResult = aggregateOutput.getResults().get(0);
Assertions.assertEquals(2, aggregateResult.getAggregateValue(countOfId)); assertEquals(2, aggregateResult.getAggregateValue(countOfId));
Assertions.assertEquals(5, aggregateResult.getAggregateValue(sumOfId)); assertEquals(5, aggregateResult.getAggregateValue(sumOfId));
Assertions.assertEquals(new BigDecimal("62.0"), aggregateResult.getAggregateValue(averageOfDaysWorked)); assertEquals(new BigDecimal("62.0"), aggregateResult.getAggregateValue(averageOfDaysWorked));
Assertions.assertEquals(new BigDecimal("26000.00"), aggregateResult.getAggregateValue(maxAnnualSalary)); assertEquals(new BigDecimal("26000.00"), aggregateResult.getAggregateValue(maxAnnualSalary));
Assertions.assertEquals("James", aggregateResult.getAggregateValue(minFirstName)); assertEquals("James", aggregateResult.getAggregateValue(minFirstName));
} }
@ -156,15 +196,15 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
{ {
AggregateResult aggregateResult = aggregateOutput.getResults().get(0); AggregateResult aggregateResult = aggregateOutput.getResults().get(0);
Assertions.assertEquals("Chamberlain", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Chamberlain", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals(2, aggregateResult.getAggregateValue(countOfId)); assertEquals(2, aggregateResult.getAggregateValue(countOfId));
Assertions.assertEquals(17, aggregateResult.getAggregateValue(sumOfDaysWorked)); assertEquals(17, aggregateResult.getAggregateValue(sumOfDaysWorked));
} }
{ {
AggregateResult aggregateResult = aggregateOutput.getResults().get(1); AggregateResult aggregateResult = aggregateOutput.getResults().get(1);
Assertions.assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals(4, aggregateResult.getAggregateValue(countOfId)); assertEquals(4, aggregateResult.getAggregateValue(countOfId));
Assertions.assertEquals(11364, aggregateResult.getAggregateValue(sumOfDaysWorked)); assertEquals(11364, aggregateResult.getAggregateValue(sumOfDaysWorked));
} }
} }
@ -201,29 +241,29 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
AggregateResult aggregateResult; AggregateResult aggregateResult;
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Chamberlain", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Chamberlain", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals("Donny", aggregateResult.getGroupByValue(firstNameGroupBy)); assertEquals("Donny", aggregateResult.getGroupByValue(firstNameGroupBy));
Assertions.assertEquals(1, aggregateResult.getAggregateValue(countOfId)); assertEquals(1, aggregateResult.getAggregateValue(countOfId));
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Chamberlain", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Chamberlain", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals("Tim", aggregateResult.getGroupByValue(firstNameGroupBy)); assertEquals("Tim", aggregateResult.getGroupByValue(firstNameGroupBy));
Assertions.assertEquals(1, aggregateResult.getAggregateValue(countOfId)); assertEquals(1, aggregateResult.getAggregateValue(countOfId));
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals("Aaron", aggregateResult.getGroupByValue(firstNameGroupBy)); assertEquals("Aaron", aggregateResult.getGroupByValue(firstNameGroupBy));
Assertions.assertEquals(1, aggregateResult.getAggregateValue(countOfId)); assertEquals(1, aggregateResult.getAggregateValue(countOfId));
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals("Darin", aggregateResult.getGroupByValue(firstNameGroupBy)); assertEquals("Darin", aggregateResult.getGroupByValue(firstNameGroupBy));
Assertions.assertEquals(2, aggregateResult.getAggregateValue(countOfId)); assertEquals(2, aggregateResult.getAggregateValue(countOfId));
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals("Trevor", aggregateResult.getGroupByValue(firstNameGroupBy)); assertEquals("Trevor", aggregateResult.getGroupByValue(firstNameGroupBy));
Assertions.assertEquals(1, aggregateResult.getAggregateValue(countOfId)); assertEquals(1, aggregateResult.getAggregateValue(countOfId));
} }
@ -255,24 +295,24 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
AggregateResult aggregateResult; AggregateResult aggregateResult;
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Kelkhoff", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals(4, aggregateResult.getAggregateValue(countOfId)); assertEquals(4, aggregateResult.getAggregateValue(countOfId));
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Richardson", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Richardson", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals(1, aggregateResult.getAggregateValue(countOfId)); assertEquals(1, aggregateResult.getAggregateValue(countOfId));
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Maes", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Maes", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals(1, aggregateResult.getAggregateValue(countOfId)); assertEquals(1, aggregateResult.getAggregateValue(countOfId));
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Samples", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Samples", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals(1, aggregateResult.getAggregateValue(countOfId)); assertEquals(1, aggregateResult.getAggregateValue(countOfId));
aggregateResult = iterator.next(); aggregateResult = iterator.next();
Assertions.assertEquals("Chamberlain", aggregateResult.getGroupByValue(lastNameGroupBy)); assertEquals("Chamberlain", aggregateResult.getGroupByValue(lastNameGroupBy));
Assertions.assertEquals(2, aggregateResult.getAggregateValue(countOfId)); assertEquals(2, aggregateResult.getAggregateValue(countOfId));
} }
@ -293,7 +333,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
AggregateResult aggregateResult = aggregateOutput.getResults().get(0); AggregateResult aggregateResult = aggregateOutput.getResults().get(0);
Assertions.assertEquals(0, aggregateResult.getAggregateValue(countOfId)); assertEquals(0, aggregateResult.getAggregateValue(countOfId));
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
// but re-run w/ a group-by -- then, if no rows are found, there are 0 result objects. // // but re-run w/ a group-by -- then, if no rows are found, there are 0 result objects. //
@ -324,12 +364,12 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true)); QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
aggregateResult = aggregateOutput.getResults().get(0); aggregateResult = aggregateOutput.getResults().get(0);
Assertions.assertEquals(43, aggregateResult.getAggregateValue(sumOfQuantity)); assertEquals(43, aggregateResult.getAggregateValue(sumOfQuantity));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1)); QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
aggregateResult = aggregateOutput.getResults().get(0); aggregateResult = aggregateOutput.getResults().get(0);
Assertions.assertEquals(33, aggregateResult.getAggregateValue(sumOfQuantity)); assertEquals(33, aggregateResult.getAggregateValue(sumOfQuantity));
} }

View File

@ -38,6 +38,7 @@ import com.kingsrook.qqq.middleware.javalin.specs.v1.MiddlewareVersionV1;
import io.javalin.Javalin; import io.javalin.Javalin;
import io.javalin.http.Context; import io.javalin.http.Context;
import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.BooleanUtils;
import org.eclipse.jetty.util.resource.Resource;
/******************************************************************************* /*******************************************************************************
@ -102,17 +103,23 @@ public class QApplicationJavalinServer
{ {
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// If you have any assets to add to the web server (e.g., logos, icons) place them at // // If you have any assets to add to the web server (e.g., logos, icons) place them at //
// src/main/resources/material-dashboard-overlay (or a directory of your choice // // src/main/resources/material-dashboard-overlay //
// under src/main/resources) and use this line of code to tell javalin about it. // // we'll use the same check that javalin (jetty?) internally uses to see if this //
// Make sure to add your app-specific directory to the javalin config before the core // // directory exists - because if it doesn't, then it'll fail to start the server... //
// material-dashboard directory, so in case the same file exists in both (e.g., // // note that that Resource object is auto-closable, hence the try-with-resources //
// favicon.png), the app-specific one will be used. //
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
config.staticFiles.add("/material-dashboard-overlay"); try(Resource resource = Resource.newClassPathResource("/material-dashboard-overlay"))
{
if(resource !=null)
{
config.staticFiles.add("/material-dashboard-overlay");
}
}
///////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
// tell javalin where to find material-dashboard static web assets // // tell javalin where to find material-dashboard static web assets //
///////////////////////////////////////////////////////////////////// // in this case, this path is coming from the qqq-frontend-material-dashboard jar //
////////////////////////////////////////////////////////////////////////////////////
config.staticFiles.add("/material-dashboard"); config.staticFiles.add("/material-dashboard");
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -68,7 +68,7 @@
<dependency> <dependency>
<groupId>com.kingsrook.qqq</groupId> <groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-frontend-material-dashboard</artifactId> <artifactId>qqq-frontend-material-dashboard</artifactId>
<version>0.20.0</version> <version>0.24.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
@ -119,12 +119,16 @@
<descriptorRefs> <descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef> <descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs> </descriptorRefs>
<archive>
<manifest>
<mainClass>com.kingsrook.sampleapp.SampleCli</mainClass>
</manifest>
</archive>
</configuration> </configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>

View File

@ -23,10 +23,8 @@ package com.kingsrook.sampleapp;
import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.middleware.javalin.QApplicationJavalinServer;
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
import com.kingsrook.sampleapp.metadata.SampleMetaDataProvider; import com.kingsrook.sampleapp.metadata.SampleMetaDataProvider;
import io.javalin.Javalin;
/******************************************************************************* /*******************************************************************************
@ -36,12 +34,6 @@ public class SampleJavalinServer
{ {
private static final QLogger LOG = QLogger.getLogger(SampleJavalinServer.class); private static final QLogger LOG = QLogger.getLogger(SampleJavalinServer.class);
private static final int PORT = 8000;
private QInstance qInstance;
private Javalin javalinService;
/******************************************************************************* /*******************************************************************************
@ -49,7 +41,7 @@ public class SampleJavalinServer
*******************************************************************************/ *******************************************************************************/
public static void main(String[] args) public static void main(String[] args)
{ {
new SampleJavalinServer().startJavalinServer(); new SampleJavalinServer().start();
} }
@ -57,38 +49,11 @@ public class SampleJavalinServer
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public void startJavalinServer() public void start()
{ {
try try
{ {
qInstance = SampleMetaDataProvider.defineInstance(); new QApplicationJavalinServer(new SampleMetaDataProvider()).start();
QJavalinImplementation qJavalinImplementation = new QJavalinImplementation(qInstance);
javalinService = Javalin.create(config ->
{
config.router.apiBuilder(qJavalinImplementation.getRoutes());
// todo - not all?
config.bundledPlugins.enableCors(cors -> cors.addRule(corsRule -> corsRule.anyHost()));
}).start(PORT);
/////////////////////////////////////////////////////////////////
// set the server to hot-swap the q instance before all routes //
/////////////////////////////////////////////////////////////////
QJavalinImplementation.setQInstanceHotSwapSupplier(() ->
{
try
{
return (SampleMetaDataProvider.defineInstance());
}
catch(Exception e)
{
LOG.warn("Error hot-swapping meta data", e);
return (null);
}
});
javalinService.before(QJavalinImplementation::hotSwapQInstance);
javalinService.after(ctx -> ctx.res().setHeader("Access-Control-Allow-Origin", "http://localhost:3000"));
} }
catch(Exception e) catch(Exception e)
{ {
@ -96,16 +61,4 @@ public class SampleJavalinServer
} }
} }
/*******************************************************************************
**
*******************************************************************************/
public void stopJavalinServer()
{
if(javalinService != null)
{
javalinService.stop();
}
}
} }

View File

@ -31,6 +31,7 @@ import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.QuickSightChartR
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QValueException; import com.kingsrook.qqq.backend.core.exceptions.QValueException;
import com.kingsrook.qqq.backend.core.instances.AbstractQQQApplication;
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher; import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter; import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
@ -78,7 +79,7 @@ import com.kingsrook.sampleapp.processes.clonepeople.ClonePeopleTransformStep;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class SampleMetaDataProvider public class SampleMetaDataProvider extends AbstractQQQApplication
{ {
public static boolean USE_MYSQL = true; public static boolean USE_MYSQL = true;
@ -108,6 +109,17 @@ public class SampleMetaDataProvider
/***************************************************************************
**
***************************************************************************/
@Override
public QInstance defineQInstance() throws QException
{
return (defineInstance());
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -145,7 +157,7 @@ public class SampleMetaDataProvider
private static void defineBranding(QInstance qInstance) private static void defineBranding(QInstance qInstance)
{ {
qInstance.setBranding(new QBrandingMetaData() qInstance.setBranding(new QBrandingMetaData()
.withLogo("/kr-logo.png") .withLogo("/samples-logo.png")
.withIcon("/kr-icon.png")); .withIcon("/kr-icon.png"));
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

@ -38,8 +38,7 @@ class SampleJavalinServerTest
void testStartStop() void testStartStop()
{ {
SampleJavalinServer sampleJavalinServer = new SampleJavalinServer(); SampleJavalinServer sampleJavalinServer = new SampleJavalinServer();
sampleJavalinServer.startJavalinServer(); sampleJavalinServer.start();
sampleJavalinServer.stopJavalinServer();
} }
} }