mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 05:30:43 +00:00
QQQ-14 some adjustments to meta-data and internal names; adding backendDetails under table meta data
This commit is contained in:
@ -43,7 +43,7 @@ public class DeleteAction
|
||||
ActionHelper.validateSession(deleteRequest);
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQModule(deleteRequest.getBackend());
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(deleteRequest.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
DeleteResult deleteResult = qModule.getDeleteInterface().execute(deleteRequest);
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
@ -43,7 +43,7 @@ public class InsertAction
|
||||
ActionHelper.validateSession(insertRequest);
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQModule(insertRequest.getBackend());
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(insertRequest.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
InsertResult insertResult = qModule.getInsertInterface().execute(insertRequest);
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
@ -43,7 +43,7 @@ public class QueryAction
|
||||
ActionHelper.validateSession(queryRequest);
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQModule(queryRequest.getBackend());
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(queryRequest.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
QueryResult queryResult = qModule.getQueryInterface().execute(queryRequest);
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
@ -43,7 +43,7 @@ public class UpdateAction
|
||||
ActionHelper.validateSession(updateRequest);
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQModule(updateRequest.getBackend());
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(updateRequest.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
UpdateResult updateResult = qModule.getUpdateInterface().execute(updateRequest);
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
@ -22,9 +22,9 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonFilter;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.serialization.QBackendMetaDataDeserializer;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -32,55 +32,32 @@ import com.fasterxml.jackson.annotation.JsonFilter;
|
||||
** NoSQL table, etc) within a qqq instance
|
||||
**
|
||||
*******************************************************************************/
|
||||
@JsonDeserialize(using = QBackendMetaDataDeserializer.class)
|
||||
public class QBackendMetaData
|
||||
{
|
||||
private String name;
|
||||
private String type;
|
||||
|
||||
@JsonFilter("secretsFilter")
|
||||
private Map<String, String> values;
|
||||
private String backendType;
|
||||
|
||||
// todo - at some point, we may want to apply this to secret properties on subclasses?
|
||||
// @JsonFilter("secretsFilter")
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Default Constructor.
|
||||
*******************************************************************************/
|
||||
public String getValue(String key)
|
||||
public QBackendMetaData()
|
||||
{
|
||||
if(values == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return values.get(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Copy Constructor. Meant for use by sub-classes. Should copy all fields!
|
||||
*******************************************************************************/
|
||||
public void setValue(String key, String value)
|
||||
protected QBackendMetaData(QBackendMetaData source)
|
||||
{
|
||||
if(values == null)
|
||||
{
|
||||
values = new HashMap<>();
|
||||
}
|
||||
values.put(key, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withValue(String key, String value)
|
||||
{
|
||||
if(values == null)
|
||||
{
|
||||
values = new HashMap<>();
|
||||
}
|
||||
values.put(key, value);
|
||||
return (this);
|
||||
this.name = source.name;
|
||||
this.backendType = source.backendType;
|
||||
}
|
||||
|
||||
|
||||
@ -117,23 +94,42 @@ public class QBackendMetaData
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for type
|
||||
** Getter for backendType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getType()
|
||||
public String getBackendType()
|
||||
{
|
||||
return type;
|
||||
return backendType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for type
|
||||
** Setter for backendType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setType(String type)
|
||||
public void setBackendType(String backendType)
|
||||
{
|
||||
this.type = type;
|
||||
this.backendType = backendType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBackendType(Class<? extends QBackendModuleInterface> backendModuleClass)
|
||||
{
|
||||
try
|
||||
{
|
||||
QBackendModuleInterface qBackendModuleInterface = backendModuleClass.getConstructor().newInstance();
|
||||
this.backendType = qBackendModuleInterface.getBackendType();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new IllegalArgumentException("Error dynamically getting backend type (name) from class [" + backendModuleClass.getName() + "], e)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -141,9 +137,9 @@ public class QBackendMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withType(String type)
|
||||
public QBackendMetaData withBackendType(String backendType)
|
||||
{
|
||||
this.type = type;
|
||||
this.backendType = backendType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -152,29 +148,9 @@ public class QBackendMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getValues()
|
||||
public QBackendMetaData withBackendType(Class<? extends QBackendModuleInterface> backendModuleClass)
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setValues(Map<String, String> values)
|
||||
{
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVales(Map<String, String> values)
|
||||
{
|
||||
this.values = values;
|
||||
setBackendType(backendModuleClass);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.backend.core.model.metadata;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.serialization.QTableBackendDetailsDeserializer;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base class where backends can specify additional per-table meta-data.
|
||||
*******************************************************************************/
|
||||
@JsonDeserialize(using = QTableBackendDetailsDeserializer.class)
|
||||
public abstract class QTableBackendDetails
|
||||
{
|
||||
private String backendType;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getBackendType()
|
||||
{
|
||||
return backendType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBackendType(String backendType)
|
||||
{
|
||||
this.backendType = backendType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for backendType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableBackendDetails withBackendType(String backendType)
|
||||
{
|
||||
this.backendType = backendType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBackendType(Class<? extends QBackendModuleInterface> backendModuleClass)
|
||||
{
|
||||
try
|
||||
{
|
||||
QBackendModuleInterface qBackendModuleInterface = backendModuleClass.getConstructor().newInstance();
|
||||
this.backendType = qBackendModuleInterface.getBackendType();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new IllegalArgumentException("Error dynamically getting backend type (name) from class [" + backendModuleClass.getName() + "], e)");
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for backendType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableBackendDetails withBackendType(Class<? extends QBackendModuleInterface> backendModuleClass)
|
||||
{
|
||||
setBackendType(backendModuleClass);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -36,8 +36,20 @@ public class QTableMetaData
|
||||
private String label;
|
||||
private String backendName;
|
||||
private String primaryKeyField;
|
||||
|
||||
private Map<String, QFieldMetaData> fields;
|
||||
|
||||
private QTableBackendDetails backendDetails;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public QTableMetaData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -246,4 +258,37 @@ public class QTableMetaData
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendDetails
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableBackendDetails getBackendDetails()
|
||||
{
|
||||
return backendDetails;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendDetails
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBackendDetails(QTableBackendDetails backendDetails)
|
||||
{
|
||||
this.backendDetails = backendDetails;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for backendDetails
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withBackendDetails(QTableBackendDetails backendDetails)
|
||||
{
|
||||
this.backendDetails = backendDetails;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.backend.core.model.metadata.frontend;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Version of QProcessMetaData that's meant for transmitting to a frontend.
|
||||
* e.g., it excludes backend-only details.
|
||||
*
|
||||
*******************************************************************************/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public class QFrontendProcessMetaData
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
private String tableName;
|
||||
private Map<String, QFrontendFieldMetaData> fields;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendProcessMetaData(QProcessMetaData processMetaData)
|
||||
{
|
||||
this.name = processMetaData.getName();
|
||||
// todo? this.label = processMetaData.getLabel();
|
||||
this.tableName = processMetaData.getTableName();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for label
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for primaryKeyField
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fields
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, QFrontendFieldMetaData> getFields()
|
||||
{
|
||||
return fields;
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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.backend.core.model.metadata.serialization;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import com.fasterxml.jackson.core.TreeNode;
|
||||
import com.fasterxml.jackson.databind.node.NullNode;
|
||||
import com.fasterxml.jackson.databind.node.TextNode;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
|
||||
import com.kingsrook.qqq.backend.core.modules.QBackendModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class DeserializerUtils
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QBackendModuleInterface getBackendModule(TreeNode treeNode) throws IOException
|
||||
{
|
||||
TreeNode backendTypeTreeNode = treeNode.get("backendType");
|
||||
if(backendTypeTreeNode == null || backendTypeTreeNode instanceof NullNode)
|
||||
{
|
||||
throw new IOException("Missing backendType in backendMetaData");
|
||||
}
|
||||
|
||||
if(!(backendTypeTreeNode instanceof TextNode textNode))
|
||||
{
|
||||
throw new IOException("backendType is not a string value (is: " + backendTypeTreeNode.getClass().getSimpleName() + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
String backendType = textNode.asText();
|
||||
|
||||
try
|
||||
{
|
||||
return new QBackendModuleDispatcher().getQBackendModule(backendType);
|
||||
}
|
||||
catch(QModuleDispatchException e)
|
||||
{
|
||||
throw (new IOException(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static <T> T reflectivelyDeserialize(Class<T> _class, TreeNode treeNode) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
T output = _class.getConstructor().newInstance();
|
||||
System.out.println("Reflectively deserializing a: " + _class.getName());
|
||||
|
||||
Map<String, Consumer<String>> setterMap = new HashMap<>();
|
||||
for(Method method : _class.getMethods())
|
||||
{
|
||||
if(method.getName().startsWith("set") && method.getParameterTypes().length == 1)
|
||||
{
|
||||
Class<?> parameterType = method.getParameterTypes()[0];
|
||||
String fieldName = method.getName().substring(3, 4).toLowerCase(Locale.ROOT) + method.getName().substring(4);
|
||||
|
||||
setterMap.put(fieldName, (String value) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
if(parameterType.equals(String.class))
|
||||
{
|
||||
method.invoke(output, value);
|
||||
}
|
||||
else if(parameterType.equals(Integer.class))
|
||||
{
|
||||
method.invoke(output, StringUtils.hasContent(value) ? Integer.parseInt(value) : null);
|
||||
}
|
||||
else if(parameterType.equals(Long.class))
|
||||
{
|
||||
method.invoke(output, StringUtils.hasContent(value) ? Long.parseLong(value) : null);
|
||||
}
|
||||
else if(parameterType.equals(BigDecimal.class))
|
||||
{
|
||||
method.invoke(output, StringUtils.hasContent(value) ? new BigDecimal(value) : null);
|
||||
}
|
||||
else if(parameterType.equals(Boolean.class))
|
||||
{
|
||||
method.invoke(output, StringUtils.hasContent(value) ? Boolean.parseBoolean(value) : null);
|
||||
}
|
||||
else if(parameterType.equals(Class.class))
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
// specifically do NOT try to handle Class type arguments //
|
||||
////////////////////////////////////////////////////////////
|
||||
}
|
||||
else if(parameterType.getPackageName().startsWith("java."))
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// if we hit this, we might want to add an else-if to handle the type //
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
throw (new RuntimeException("Field " + fieldName + " is of an unhandled type " + parameterType.getName() + " when deserializing " + _class.getName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////
|
||||
// gracefully ignore other types. //
|
||||
////////////////////////////////////
|
||||
}
|
||||
}
|
||||
catch(IllegalAccessException | InvocationTargetException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DeserializerUtils.deserializeBean(treeNode, setterMap);
|
||||
|
||||
return output;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new IOException("Error reflectively deserializing table details", e));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper for custom jackson serializers - allows the caller to specify a map
|
||||
** of field names to setter methods.
|
||||
**
|
||||
** Note, the consumers in the map all work on strings, so you may need to do
|
||||
** Integer.parseInt, for example, in a lambda in the map.
|
||||
*******************************************************************************/
|
||||
private static void deserializeBean(TreeNode treeNode, Map<String, Consumer<String>> setterMap) throws IOException
|
||||
{
|
||||
Iterator<String> fieldNamesIterator = treeNode.fieldNames();
|
||||
while(fieldNamesIterator.hasNext())
|
||||
{
|
||||
String fieldName = fieldNamesIterator.next();
|
||||
System.out.println("Handling field [" + fieldName + "]");
|
||||
|
||||
if(!setterMap.containsKey(fieldName))
|
||||
{
|
||||
throw (new IllegalArgumentException("Unexpected value: " + fieldName));
|
||||
}
|
||||
|
||||
TreeNode fieldNode = treeNode.get(fieldName);
|
||||
if(fieldNode instanceof NullNode)
|
||||
{
|
||||
setterMap.get(fieldName).accept(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
setterMap.get(fieldName).accept(((TextNode) fieldNode).asText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.backend.core.model.metadata.serialization;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.TreeNode;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Jackson custom deserialization class, to return an appropriate sub-type of
|
||||
** A QBackendMetaData, based on the backendType specified within.
|
||||
*******************************************************************************/
|
||||
public class QBackendMetaDataDeserializer extends JsonDeserializer<QBackendMetaData>
|
||||
{
|
||||
@Override
|
||||
public QBackendMetaData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException
|
||||
{
|
||||
TreeNode treeNode = jsonParser.readValueAsTree();
|
||||
QBackendModuleInterface backendModule = DeserializerUtils.getBackendModule(treeNode);
|
||||
return DeserializerUtils.reflectivelyDeserialize(backendModule.getBackendMetaDataClass(), treeNode);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.backend.core.model.metadata.serialization;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.TreeNode;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableBackendDetails;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Jackson custom deserialization class, to return an appropriate sub-type of
|
||||
** QTableBackendDetails, based on the backendType of the containing table.
|
||||
*******************************************************************************/
|
||||
public class QTableBackendDetailsDeserializer extends JsonDeserializer<QTableBackendDetails>
|
||||
{
|
||||
@Override
|
||||
public QTableBackendDetails deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException
|
||||
{
|
||||
TreeNode treeNode = jsonParser.readValueAsTree();
|
||||
QBackendModuleInterface backendModule = DeserializerUtils.getBackendModule(treeNode);
|
||||
return DeserializerUtils.reflectivelyDeserialize(backendModule.getTableBackendDetailsClass(), treeNode);
|
||||
}
|
||||
|
||||
}
|
@ -27,6 +27,8 @@ import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -38,6 +40,8 @@ import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface
|
||||
*******************************************************************************/
|
||||
public class QBackendModuleDispatcher
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(QBackendModuleDispatcher.class);
|
||||
|
||||
private Map<String, String> backendTypeToModuleClassNameMap;
|
||||
|
||||
|
||||
@ -48,9 +52,29 @@ public class QBackendModuleDispatcher
|
||||
public QBackendModuleDispatcher()
|
||||
{
|
||||
backendTypeToModuleClassNameMap = new HashMap<>();
|
||||
backendTypeToModuleClassNameMap.put("mock", "com.kingsrook.qqq.backend.core.modules.mock.MockBackendModule");
|
||||
backendTypeToModuleClassNameMap.put("rdbms", "com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule");
|
||||
// todo - let user define custom type -> classes
|
||||
|
||||
String[] moduleClassNames = new String[]
|
||||
{
|
||||
// todo - let modules somehow "export" their types here?
|
||||
// e.g., backend-core shouldn't need to "know" about the modules.
|
||||
"com.kingsrook.qqq.backend.core.modules.mock.MockBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.filesystem.FilesystemBackendModule"
|
||||
};
|
||||
|
||||
for(String moduleClassName : moduleClassNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> moduleClass = Class.forName(moduleClassName);
|
||||
QBackendModuleInterface module = (QBackendModuleInterface) moduleClass.getConstructor().newInstance();
|
||||
backendTypeToModuleClassNameMap.put(module.getBackendType(), moduleClassName);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.debug("Backend module [{}] could not be loaded: {}", moduleClassName, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -58,14 +82,24 @@ public class QBackendModuleDispatcher
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendModuleInterface getQModule(QBackendMetaData backend) throws QModuleDispatchException
|
||||
public QBackendModuleInterface getQBackendModule(QBackendMetaData backend) throws QModuleDispatchException
|
||||
{
|
||||
return (getQBackendModule(backend.getBackendType()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendModuleInterface getQBackendModule(String backendType) throws QModuleDispatchException
|
||||
{
|
||||
try
|
||||
{
|
||||
String className = backendTypeToModuleClassNameMap.get(backend.getType());
|
||||
if (className == null)
|
||||
String className = backendTypeToModuleClassNameMap.get(backendType);
|
||||
if(className == null)
|
||||
{
|
||||
throw (new QModuleDispatchException("Unrecognized backend type [" + backend.getType() + "] in dispatcher."));
|
||||
throw (new QModuleDispatchException("Unrecognized backend type [" + backendType + "] in dispatcher."));
|
||||
}
|
||||
|
||||
Class<?> moduleClass = Class.forName(className);
|
||||
@ -77,7 +111,7 @@ public class QBackendModuleDispatcher
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QModuleDispatchException("Error getting backend module of type: " + backend.getType(), e));
|
||||
throw (new QModuleDispatchException("Error getting backend module of type: " + backendType, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,15 +22,37 @@
|
||||
package com.kingsrook.qqq.backend.core.modules.interfaces;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableBackendDetails;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface that a QBackendModule must implement.
|
||||
**
|
||||
** Note, methods all have a default version, which throws a 'not implemented'
|
||||
** Note, some methods all have a default version, which throws a 'not implemented'
|
||||
** exception.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface QBackendModuleInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Method where a backend module must be able to provide its type (name).
|
||||
*******************************************************************************/
|
||||
String getBackendType();
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for backend meta data for this module.
|
||||
*******************************************************************************/
|
||||
Class<? extends QBackendMetaData> getBackendMetaDataClass();
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for table-backend details for this module.
|
||||
*******************************************************************************/
|
||||
default Class<? extends QTableBackendDetails> getTableBackendDetailsClass()
|
||||
{
|
||||
return QTableBackendDetails.class;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -74,4 +96,5 @@ public interface QBackendModuleInterface
|
||||
{
|
||||
throw new IllegalStateException(actionName + " is not implemented in this module: " + this.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.modules.mock;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.DeleteInterface;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.InsertInterface;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
|
||||
@ -38,6 +39,28 @@ import com.kingsrook.qqq.backend.core.modules.interfaces.UpdateInterface;
|
||||
*******************************************************************************/
|
||||
public class MockBackendModule implements QBackendModuleInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Method where a backend module must be able to provide its type (name).
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getBackendType()
|
||||
{
|
||||
return ("mock");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for backend meta data for this module.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<? extends QBackendMetaData> getBackendMetaDataClass()
|
||||
{
|
||||
return (QBackendMetaData.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -59,6 +82,7 @@ public class MockBackendModule implements QBackendModuleInterface
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -69,6 +93,7 @@ public class MockBackendModule implements QBackendModuleInterface
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@ -94,6 +95,30 @@ public class JsonUtils
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Serialize any object into a "pretty" / formatted JSON String.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String prettyPrint(String json)
|
||||
{
|
||||
try
|
||||
{
|
||||
ObjectMapper mapper = newObjectMapper();
|
||||
Object object = mapper.reader().readValue(json, Map.class);
|
||||
ObjectWriter objectWriter = mapper.writerWithDefaultPrettyPrinter();
|
||||
String jsonResult = objectWriter.writeValueAsString(object);
|
||||
return (jsonResult);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error pretty printing JSON string", e);
|
||||
throw new IllegalArgumentException("Error in pretty printing JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** De-serialize a json string into an object of the specified class.
|
||||
**
|
||||
@ -103,8 +128,7 @@ public class JsonUtils
|
||||
public static <T> T toObject(String json, Class<T> targetClass) throws IOException
|
||||
{
|
||||
ObjectMapper objectMapper = newObjectMapper();
|
||||
T t = objectMapper.reader().readValue(json, targetClass);
|
||||
return t;
|
||||
return objectMapper.reader().readValue(json, targetClass);
|
||||
}
|
||||
|
||||
|
||||
@ -118,8 +142,7 @@ public class JsonUtils
|
||||
public static <T> T toObject(String json, TypeReference<T> typeReference) throws IOException
|
||||
{
|
||||
ObjectMapper objectMapper = newObjectMapper();
|
||||
T t = objectMapper.readValue(json, typeReference);
|
||||
return t;
|
||||
return objectMapper.readValue(json, typeReference);
|
||||
}
|
||||
|
||||
|
||||
@ -192,4 +215,5 @@ public class JsonUtils
|
||||
{
|
||||
return (json != null && json.matches("(?s)\\s*\\[.*"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class QBackendModuleDispatcherTest
|
||||
@Test
|
||||
public void test_getQModule_valid() throws QModuleDispatchException
|
||||
{
|
||||
QBackendModuleInterface qModule = new QBackendModuleDispatcher().getQModule(TestUtils.defineBackend());
|
||||
QBackendModuleInterface qModule = new QBackendModuleDispatcher().getQBackendModule(TestUtils.defineBackend());
|
||||
assertNotNull(qModule);
|
||||
}
|
||||
|
||||
@ -60,8 +60,8 @@ class QBackendModuleDispatcherTest
|
||||
assertThrows(QModuleDispatchException.class, () ->
|
||||
{
|
||||
QBackendMetaData qBackendMetaData = TestUtils.defineBackend();
|
||||
qBackendMetaData.setType("aTypeThatWontEverExist");
|
||||
new QBackendModuleDispatcher().getQModule(qBackendMetaData);
|
||||
qBackendMetaData.setBackendType("aTypeThatWontEverExist");
|
||||
new QBackendModuleDispatcher().getQBackendModule(qBackendMetaData);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ public class TestUtils
|
||||
{
|
||||
return new QBackendMetaData()
|
||||
.withName("default")
|
||||
.withType("mock");
|
||||
.withBackendType("mock");
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,9 +150,8 @@
|
||||
},
|
||||
"backends": {
|
||||
"default": {
|
||||
"values": null,
|
||||
"name": "default",
|
||||
"type": "mock"
|
||||
"backendType": "mock"
|
||||
}
|
||||
},
|
||||
"authentication": {
|
||||
|
Reference in New Issue
Block a user