Auto-adorn PVS tables with link-to-record; change query to fetch instants, not LocalDateTimes

This commit is contained in:
2022-09-27 18:59:58 -05:00
parent 3587cc0676
commit 4d16bc0fc7
6 changed files with 130 additions and 13 deletions

View File

@ -34,12 +34,16 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
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.fields.AdornmentType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; 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.metadata.layout.QAppChildMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType; import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
@ -73,12 +77,24 @@ public class QInstanceEnricher
{ {
private static final Logger LOG = LogManager.getLogger(QInstanceEnricher.class); private static final Logger LOG = LogManager.getLogger(QInstanceEnricher.class);
private final QInstance qInstance;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public void enrich(QInstance qInstance) public QInstanceEnricher(QInstance qInstance)
{
this.qInstance = qInstance;
}
/*******************************************************************************
**
*******************************************************************************/
public void enrich()
{ {
if(qInstance.getTables() != null) if(qInstance.getTables() != null)
{ {
@ -210,6 +226,61 @@ public class QInstanceEnricher
{ {
field.setLabel(nameToLabel(field.getName())); field.setLabel(nameToLabel(field.getName()));
} }
/////////////////////////////////////////////////////////////////////////
// if this field has a possibleValueSource //
// and that PVS exists in the instance //
// and it's a table-type PVS and the table name is set //
// and it's a valid table in the instant, and the table is in some app //
// and the field doesn't have a LINK adornment //
// then add a link-to-record-from-table adornment to the field. //
/////////////////////////////////////////////////////////////////////////
if(StringUtils.hasContent(field.getPossibleValueSourceName()))
{
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(field.getPossibleValueSourceName());
if(possibleValueSource != null)
{
String tableName = possibleValueSource.getTableName();
if(QPossibleValueSourceType.TABLE.equals(possibleValueSource.getType()) && StringUtils.hasContent(tableName))
{
if(qInstance.getTable(tableName) != null && doesAnyAppHaveTable(tableName))
{
if(field.getAdornments() == null || field.getAdornments().stream().noneMatch(a -> AdornmentType.LINK.equals(a.getType())))
{
field.withFieldAdornment(new FieldAdornment().withType(AdornmentType.LINK)
.withValue(AdornmentType.LinkValues.TO_RECORD_FROM_TABLE, tableName));
}
}
}
}
}
}
/*******************************************************************************
**
*******************************************************************************/
private boolean doesAnyAppHaveTable(String tableName)
{
if(qInstance.getApps() != null)
{
for(QAppMetaData app : qInstance.getApps().values())
{
if(app.getChildren() != null)
{
for(QAppChildMetaData child : app.getChildren())
{
if(child instanceof QTableMetaData && tableName.equals(child.getName()))
{
return (true);
}
}
}
}
}
return (false);
} }

View File

@ -94,7 +94,7 @@ public class QInstanceValidator
// before validation, enrich the object (e.g., to fill in values that the user doesn't have to // // before validation, enrich the object (e.g., to fill in values that the user doesn't have to //
///////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////
// TODO - possible point of customization (use a different enricher, or none, or pass it options). // TODO - possible point of customization (use a different enricher, or none, or pass it options).
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
} }
catch(Exception e) catch(Exception e)
{ {

View File

@ -44,6 +44,7 @@ public enum AdornmentType
public interface LinkValues public interface LinkValues
{ {
String TARGET = "target"; String TARGET = "target";
String TO_RECORD_FROM_TABLE = "toRecordFromTable";
} }

View File

@ -25,9 +25,13 @@ package com.kingsrook.qqq.backend.core.instances;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
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.fields.AdornmentType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; 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.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection; import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier; import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
@ -39,6 +43,7 @@ import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_PEOPLE;
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.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;
/******************************************************************************* /*******************************************************************************
@ -59,7 +64,7 @@ class QInstanceEnricherTest
QTableMetaData personTable = qInstance.getTable("person"); QTableMetaData personTable = qInstance.getTable("person");
personTable.setLabel(null); personTable.setLabel(null);
assertNull(personTable.getLabel()); assertNull(personTable.getLabel());
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
assertEquals("Person", personTable.getLabel()); assertEquals("Person", personTable.getLabel());
} }
@ -79,7 +84,7 @@ class QInstanceEnricherTest
personTable.setName(null); personTable.setName(null);
assertNull(personTable.getLabel()); assertNull(personTable.getLabel());
assertNull(personTable.getName()); assertNull(personTable.getName());
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
assertNull(personTable.getLabel()); assertNull(personTable.getLabel());
assertNull(personTable.getName()); assertNull(personTable.getName());
} }
@ -97,7 +102,7 @@ class QInstanceEnricherTest
QFieldMetaData idField = qInstance.getTable("person").getField("id"); QFieldMetaData idField = qInstance.getTable("person").getField("id");
idField.setLabel(null); idField.setLabel(null);
assertNull(idField.getLabel()); assertNull(idField.getLabel());
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
assertEquals("Id", idField.getLabel()); assertEquals("Id", idField.getLabel());
} }
@ -118,7 +123,7 @@ class QInstanceEnricherTest
.withFieldNames(new ArrayList<>(personTable.getFields().keySet())) .withFieldNames(new ArrayList<>(personTable.getFields().keySet()))
)); ));
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
assertEquals("Test", personTable.getSections().get(0).getLabel()); assertEquals("Test", personTable.getSections().get(0).getLabel());
} }
@ -198,7 +203,7 @@ class QInstanceEnricherTest
void testGenerateAppSections() void testGenerateAppSections()
{ {
QInstance qInstance = TestUtils.defineInstance(); QInstance qInstance = TestUtils.defineInstance();
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
assertNotNull(qInstance.getApp(APP_NAME_GREETINGS).getSections()); assertNotNull(qInstance.getApp(APP_NAME_GREETINGS).getSections());
assertEquals(1, qInstance.getApp(APP_NAME_GREETINGS).getSections().size(), "App should automatically have one section"); assertEquals(1, qInstance.getApp(APP_NAME_GREETINGS).getSections().size(), "App should automatically have one section");
assertEquals(0, qInstance.getApp(APP_NAME_GREETINGS).getSections().get(0).getTables().size(), "Section should not have tables"); assertEquals(0, qInstance.getApp(APP_NAME_GREETINGS).getSections().get(0).getTables().size(), "Section should not have tables");
@ -228,18 +233,57 @@ class QInstanceEnricherTest
{ {
QInstance qInstance = TestUtils.defineInstance(); QInstance qInstance = TestUtils.defineInstance();
QTableMetaData table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields(new ArrayList<>()); QTableMetaData table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields(new ArrayList<>());
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
assertNull(table.getRecordLabelFormat()); assertNull(table.getRecordLabelFormat());
qInstance = TestUtils.defineInstance(); qInstance = TestUtils.defineInstance();
table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName"); table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName");
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
assertEquals("%s", table.getRecordLabelFormat()); assertEquals("%s", table.getRecordLabelFormat());
qInstance = TestUtils.defineInstance(); qInstance = TestUtils.defineInstance();
table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName", "lastName"); table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName", "lastName");
new QInstanceEnricher().enrich(qInstance); new QInstanceEnricher(qInstance).enrich();
assertEquals("%s %s", table.getRecordLabelFormat()); assertEquals("%s %s", table.getRecordLabelFormat());
} }
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAddTablePvsAdornment()
{
///////////////////////////////////////////////////////////////////////////////////////////////////////
// first make sure the adornment doesn't get added for favoriteShapeId, because it isn't in any apps //
///////////////////////////////////////////////////////////////////////////////////////////////////////
{
QInstance qInstance = TestUtils.defineInstance();
QTableMetaData personTable = qInstance.getTable("person");
QFieldMetaData favoriteShapeId = personTable.getField("favoriteShapeId");
new QInstanceEnricher(qInstance).enrich();
assertNull(favoriteShapeId.getAdornments());
}
////////////////////////////////////////////////////////////////////
// then put shape table in an app, re-run, and see it get adorned //
////////////////////////////////////////////////////////////////////
{
QInstance qInstance = TestUtils.defineInstance();
QTableMetaData shapeTable = qInstance.getTable(TestUtils.TABLE_NAME_SHAPE);
QAppMetaData miscApp = qInstance.getApp(APP_NAME_MISCELLANEOUS);
miscApp.addChild(shapeTable);
QTableMetaData personTable = qInstance.getTable("person");
QFieldMetaData favoriteShapeId = personTable.getField("favoriteShapeId");
new QInstanceEnricher(qInstance).enrich();
assertNotNull(favoriteShapeId.getAdornments());
Optional<FieldAdornment> optionalAdornment = favoriteShapeId.getAdornments().stream().filter(a -> a.getType().equals(AdornmentType.LINK)).findFirst();
assertTrue(optionalAdornment.isPresent());
FieldAdornment adornment = optionalAdornment.get();
assertEquals("shape", adornment.getValues().get(AdornmentType.LinkValues.TO_RECORD_FROM_TABLE));
}
}
} }

View File

@ -204,7 +204,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
} }
case DATE_TIME: case DATE_TIME:
{ {
return (QueryManager.getLocalDateTime(resultSet, i)); return (QueryManager.getInstant(resultSet, i));
} }
case BOOLEAN: case BOOLEAN:
{ {

View File

@ -231,7 +231,8 @@ public class QJavalinProcessHandler
if(userFacingException != null) if(userFacingException != null)
{ {
LOG.info("User-facing exception in process", userFacingException); LOG.info("User-facing exception in process", userFacingException);
resultForCaller.put("error", userFacingException.getMessage()); // todo - put this somewhere else (make error an object w/ user-facing and/or other error?) resultForCaller.put("error", userFacingException.getMessage());
resultForCaller.put("userFacingError", userFacingException.getMessage());
} }
else else
{ {