Initial add of MetaDataProducer

This commit is contained in:
2023-04-12 11:18:32 -05:00
parent 51b2a9d924
commit 5453e2e081
9 changed files with 394 additions and 4 deletions

View File

@ -0,0 +1,53 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.backend.core.model.metadata;
import com.kingsrook.qqq.backend.core.exceptions.QException;
/*******************************************************************************
** Abstract class that knows how to produce meta data objects. Useful with
** MetaDataProducerHelper, to put point at a package full of these, and populate
** your whole QInstance.
*******************************************************************************/
public abstract class MetaDataProducer<T extends TopLevelMetaDataInterface>
{
/*******************************************************************************
** Produce the metaData object. Generally, you don't want to add it to the instance
** yourself - but the instance is there in case you need it to get other metaData.
*******************************************************************************/
public abstract T produce(QInstance qInstance) throws QException;
/*******************************************************************************
** In case this producer needs to run before (or after) others, this method
** can help influence that (e.g., if used by MetaDataProducerHelper).
*******************************************************************************/
public int getSortOrder()
{
return (500);
}
}

View File

@ -0,0 +1,154 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.backend.core.model.metadata;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
/*******************************************************************************
** Help work with MetaDataProducers.
*******************************************************************************/
public class MetaDataProducerHelper
{
private static final QLogger LOG = QLogger.getLogger(MetaDataProducerHelper.class);
/*******************************************************************************
** Recursively find all classes in the given package, that extend MetaDataProducer,
** run them, and add their output to the given qInstance.
**
** Note - they'll be sorted by the sortOrder they provide.
*******************************************************************************/
public static void processAllMetaDataProducersInPackage(QInstance instance, String packageName)
{
////////////////////////////////////////////////////////////
// find all the meta data producer classes in the package //
////////////////////////////////////////////////////////////
List<Class<?>> classesInPackage = getClassesInPackage(packageName);
List<MetaDataProducer<?>> producers = new ArrayList<>();
for(Class<?> aClass : classesInPackage)
{
try
{
if(Modifier.isAbstract(aClass.getModifiers()))
{
continue;
}
for(Constructor<?> constructor : aClass.getConstructors())
{
if(constructor.getParameterCount() == 0)
{
Object o = constructor.newInstance();
if(o instanceof MetaDataProducer<?> metaDataProducer)
{
producers.add(metaDataProducer);
}
break;
}
}
}
catch(Exception e)
{
LOG.info("Error adding metaData from producer", logPair("producer", aClass.getSimpleName()), e);
}
}
/////////////////////////////
// sort them by sort order //
/////////////////////////////
producers.sort(Comparator.comparing(p -> p.getSortOrder()));
//////////////////////////////////////////////////////////////
// execute each one, adding their meta data to the instance //
//////////////////////////////////////////////////////////////
for(MetaDataProducer<?> producer : producers)
{
try
{
TopLevelMetaDataInterface metaData = producer.produce(instance);
if(metaData != null)
{
metaData.addSelfToInstance(instance);
}
}
catch(Exception e)
{
LOG.warn("error executing metaDataProducer", logPair("producer", producer.getClass().getSimpleName()), e);
}
}
}
/*******************************************************************************
** Thanks, Chat GPT.
*******************************************************************************/
private static List<Class<?>> getClassesInPackage(String packageName)
{
List<Class<?>> classes = new ArrayList<>();
String path = packageName.replace('.', '/');
File directory = new File(Thread.currentThread().getContextClassLoader().getResource(path).getFile());
if(directory.exists())
{
File[] files = directory.listFiles();
if(files != null)
{
for(File file : files)
{
if(file.isFile() && file.getName().endsWith(".class"))
{
String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
try
{
classes.add(Class.forName(className));
}
catch(ClassNotFoundException e)
{
// Ignore, class not found
}
}
else if(file.isDirectory())
{
List<Class<?>> subClasses = getClassesInPackage(packageName + "." + file.getName());
classes.addAll(subClasses);
}
}
}
}
return (classes);
}
}

View File

@ -0,0 +1,37 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.backend.core.model.metadata;
/*******************************************************************************
** Interface for meta-data classes that can be added directly (e.g, at the top
** level) to a QInstance (such as a QTableMetaData - not a QFieldMetaData).
*******************************************************************************/
public interface TopLevelMetaDataInterface
{
/*******************************************************************************
**
*******************************************************************************/
void addSelfToInstance(QInstance qInstance);
}

View File

@ -25,13 +25,15 @@ package com.kingsrook.qqq.backend.core.model.metadata.joins;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils;
/******************************************************************************* /*******************************************************************************
** Definition of how 2 tables join together within a QQQ Instance. ** Definition of how 2 tables join together within a QQQ Instance.
*******************************************************************************/ *******************************************************************************/
public class QJoinMetaData public class QJoinMetaData implements TopLevelMetaDataInterface
{ {
private String name; private String name;
private JoinType type; private JoinType type;
@ -317,4 +319,15 @@ public class QJoinMetaData
} }
return (leftTable + "Join" + StringUtils.ucFirst(rightTable)); return (leftTable + "Join" + StringUtils.ucFirst(rightTable));
} }
/*******************************************************************************
**
*******************************************************************************/
@Override
public void addSelfToInstance(QInstance qInstance)
{
qInstance.addJoin(this);
}
} }

View File

@ -25,6 +25,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.possiblevalues;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
@ -34,7 +36,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
** table or an enum). ** table or an enum).
** **
*******************************************************************************/ *******************************************************************************/
public class QPossibleValueSource public class QPossibleValueSource implements TopLevelMetaDataInterface
{ {
private String name; private String name;
private String label; private String label;
@ -587,4 +589,15 @@ public class QPossibleValueSource
return (this); return (this);
} }
/*******************************************************************************
**
*******************************************************************************/
@Override
public void addSelfToInstance(QInstance qInstance)
{
qInstance.addPossibleValueSource(this);
}
} }

View File

@ -29,6 +29,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
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.layout.QAppChildMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
@ -43,7 +45,7 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
** Meta-Data to define a process in a QQQ instance. ** Meta-Data to define a process in a QQQ instance.
** **
*******************************************************************************/ *******************************************************************************/
public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissionRules public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissionRules, TopLevelMetaDataInterface
{ {
private String name; private String name;
private String label; private String label;
@ -531,4 +533,15 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
return (this); return (this);
} }
/*******************************************************************************
**
*******************************************************************************/
@Override
public void addSelfToInstance(QInstance qInstance)
{
qInstance.addProcess(this);
}
} }

View File

@ -37,6 +37,8 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity; import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntityField; import com.kingsrook.qqq.backend.core.model.data.QRecordEntityField;
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.TopLevelMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules; import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
@ -53,7 +55,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf;
** Meta-Data to define a table in a QQQ instance. ** Meta-Data to define a table in a QQQ instance.
** **
*******************************************************************************/ *******************************************************************************/
public class QTableMetaData implements QAppChildMetaData, Serializable, MetaDataWithPermissionRules public class QTableMetaData implements QAppChildMetaData, Serializable, MetaDataWithPermissionRules, TopLevelMetaDataInterface
{ {
private String name; private String name;
private String label; private String label;
@ -1284,4 +1286,14 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
return (this); return (this);
} }
/*******************************************************************************
**
*******************************************************************************/
@Override
public void addSelfToInstance(QInstance qInstance)
{
qInstance.addTable(this);
}
} }

View File

@ -0,0 +1,47 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.backend.core.model.metadata;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestMetaDataProducer;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for MetaDataProducerHelper
*******************************************************************************/
class MetaDataProducerHelperTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test()
{
QInstance qInstance = new QInstance();
MetaDataProducerHelper.processAllMetaDataProducersInPackage(qInstance, "com.kingsrook.qqq.backend.core.model.metadata.producers");
assertTrue(qInstance.getTables().containsKey(TestMetaDataProducer.NAME));
}
}

View File

@ -0,0 +1,48 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.backend.core.model.metadata.producers;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
**
*******************************************************************************/
public class TestMetaDataProducer extends MetaDataProducer<QTableMetaData>
{
public static final String NAME = "BuiltByProducer";
/*******************************************************************************
**
*******************************************************************************/
@Override
public QTableMetaData produce(QInstance qInstance) throws QException
{
return new QTableMetaData().withName(NAME);
}
}