feat(model): add clone method to QTableMEtaData and all composed classes #32

Merged
darinkelkhoff merged 1 commits from feature/table-meta-data-clone into develop 2025-10-16 01:30:51 +00:00
12 changed files with 493 additions and 0 deletions

No files matched your search

+11
View File
@@ -34,5 +34,16 @@ export class QPossibleValue
this.id = object.id;
this.label = object.label;
}
/***************************************************************************
*
***************************************************************************/
public clone(): QPossibleValue
{
const clone = new QPossibleValue({
...this
});
return (clone);
}
}
+21
View File
@@ -53,4 +53,25 @@ export class FieldAdornment
}
return (null);
}
/***************************************************************************
*
***************************************************************************/
public clone(): FieldAdornment
{
const valuesClone = this.values ? new Map<string, any>() : undefined;
if(this.values && valuesClone)
{
this.values.forEach((value, key) =>
{
valuesClone.set(key, value);
});
}
const clone = new FieldAdornment({
...this,
values: valuesClone
});
return (clone);
}
}
+21
View File
@@ -45,4 +45,25 @@ export class QExposedJoin
this.joinPath.push(new QJoinMetaData(object.joinPath[i]));
}
}
/***************************************************************************
*
***************************************************************************/
public clone(): QExposedJoin
{
const joinTableClone = (this.joinTable as any).clone();
const joinPathClone: QJoinMetaData[] = [];
for (let joinMetaData of this.joinPath)
{
joinPathClone.push((joinMetaData as any).clone());
}
const clone = new QExposedJoin({
...this,
joinTable: joinTableClone,
joinPath: joinPathClone
});
return (clone);
}
}
+55
View File
@@ -135,4 +135,59 @@ export class QFieldMetaData
return (null);
}
/***************************************************************************
*
***************************************************************************/
public clone(): QFieldMetaData
{
const inlinePossibleValueSourceClone: QPossibleValue[] | undefined = this.inlinePossibleValueSource ? [] : undefined;
if(this.inlinePossibleValueSource && inlinePossibleValueSourceClone)
{
for (let qPossibleValue of this.inlinePossibleValueSource)
{
inlinePossibleValueSourceClone.push((qPossibleValue as any).clone());
}
}
const adornmentsClone: FieldAdornment[] | undefined = this.adornments ? [] : undefined;
if(this.adornments && adornmentsClone)
{
for (let adornment of this.adornments)
{
adornmentsClone.push((adornment as any).clone());
}
}
const helpContentsClone: QHelpContent[] | undefined = this.helpContents ? [] : undefined;
if(this.helpContents && helpContentsClone)
{
for (let helpContent of this.helpContents)
{
helpContentsClone.push((helpContent as any).clone());
}
}
const behaviorsClone: any[] | undefined = this.behaviors ? [...this.behaviors] : undefined;
const supplementalFieldMetaDataClone: Map<String, any> = new Map();
this.supplementalFieldMetaData.forEach((value, key) =>
{
supplementalFieldMetaDataClone.set(key, value);
});
const possibleValueSourceFilterClone = this.possibleValueSourceFilter ? (this.possibleValueSourceFilter as any).clone() : undefined;
const clone = new QFieldMetaData({
...this,
inlinePossibleValueSource: inlinePossibleValueSourceClone,
adornments: adornmentsClone,
helpContents: helpContentsClone,
behaviors: behaviorsClone,
supplementalFieldMetaData: supplementalFieldMetaDataClone,
possibleValueSourceFilter: possibleValueSourceFilterClone
});
return (clone);
}
}
+18
View File
@@ -105,4 +105,22 @@ export class QHelpContent
return (rs);
}
/***************************************************************************
*
***************************************************************************/
public clone(): QHelpContent
{
const rolesClone = new Set<string>();
this.roles.forEach((role) =>
{
rolesClone.add(role);
});
const clone = new QHelpContent({
...this,
roles: rolesClone
});
return (clone);
}
}
+11
View File
@@ -37,4 +37,15 @@ export class QJoinMetaData
this.leftTable = object.leftTable;
this.rightTable = object.rightTable;
}
/***************************************************************************
*
***************************************************************************/
public clone(): QJoinMetaData
{
const clone = new QJoinMetaData({
...this
});
return (clone);
}
}
+73
View File
@@ -111,4 +111,77 @@ export class QTableMetaData
this.helpContent = QHelpContent.buildMap(object.helpContents);
}
/***************************************************************************
*
***************************************************************************/
public clone(): QTableMetaData
{
const fieldsClone = this.fields ? new Map() : undefined;
if(this.fields && fieldsClone)
{
this.fields.forEach((field, name) =>
{
fieldsClone.set(name, field.clone());
})
}
const sectionsClone: QTableSection[] | undefined = this.sections ? [] : undefined;
if(this.sections && sectionsClone)
{
for (let section of this.sections)
{
sectionsClone.push((section as any).clone());
}
}
const exposedJoinsClone: QExposedJoin[] | undefined = this.exposedJoins ? [] : undefined;
if(this.exposedJoins && exposedJoinsClone)
{
for (let exposedJoin of this.exposedJoins)
{
exposedJoinsClone.push((exposedJoin as any).clone());
}
}
const capabilitiesClone = this.capabilities ? new Set() : undefined;
if(this.capabilities && capabilitiesClone)
{
this.capabilities.forEach((capability) =>
{
capabilitiesClone.add(capability);
});
}
const helpContentsClone = this.helpContent ? new Map() : undefined;
if(this.helpContent && helpContentsClone)
{
this.helpContent.forEach((helpContentArray, name) =>
{
if(helpContentArray)
{
const helpContentArrayClone: QHelpContent[] = [];
for (let qHelpContent of helpContentArray)
{
helpContentArrayClone.push((qHelpContent as any).clone())
}
helpContentsClone.set(name, helpContentArrayClone);
}
});
}
const clone = new QTableMetaData({
...this,
fields: fieldsClone,
sections: sectionsClone,
exposedJoins: exposedJoinsClone,
capabilities: capabilitiesClone,
supplementalTableMetaData: new Map(this.supplementalTableMetaData),
helpContent: helpContentsClone
});
return (clone);
}
}
+21
View File
@@ -60,4 +60,25 @@ export class QTableSection
this.helpContents = QHelpContent.buildArray(object.helpContents)
}
/***************************************************************************
*
***************************************************************************/
public clone(): QTableSection
{
const helpContentsClone: QHelpContent[] = (this.helpContents ? [] : undefined) as QHelpContent[];
if(this.helpContents && helpContentsClone)
{
for (let helpContent of this.helpContents)
{
helpContentsClone.push((helpContent as any).clone());
}
}
const clone = new QTableSection({
...this,
helpContents: helpContentsClone
});
return (clone);
}
}
+14
View File
@@ -38,4 +38,18 @@ export class QFilterCriteria
this.operator = operator;
this.values = values;
}
/***************************************************************************
*
***************************************************************************/
public clone(): QFilterCriteria
{
const clone = new QFilterCriteria(
this.fieldName,
this.operator,
[...this.values]
);
clone.otherFieldName = this.otherFieldName;
return (clone);
}
}
+12
View File
@@ -33,4 +33,16 @@ export class QFilterOrderBy
this.fieldName = fieldName;
this.isAscending = isAscending;
}
/***************************************************************************
*
***************************************************************************/
public clone(): QFilterOrderBy
{
const clone = new QFilterOrderBy(
this.fieldName,
this.isAscending
);
return (clone);
}
}
+43
View File
@@ -107,4 +107,47 @@ export class QQueryFilter
}
this.subFilters.push(subFilter);
}
/***************************************************************************
*
***************************************************************************/
public clone(): QQueryFilter
{
const criteriaClone: QFilterCriteria[] | undefined = this.criteria ? [] : undefined;
if(this.criteria && criteriaClone)
{
for (let criteria of this.criteria)
{
criteriaClone.push((criteria as any).clone());
}
}
const orderBysClone: QFilterOrderBy[] | undefined = this.orderBys ? [] : undefined;
if(this.orderBys && orderBysClone)
{
for (let orderBy of this.orderBys)
{
orderBysClone.push((orderBy as any).clone());
}
}
const subFiltersClone: QQueryFilter[] | undefined = this.subFilters ? [] : undefined;
if(this.subFilters && subFiltersClone)
{
for (let subFilter of this.subFilters)
{
subFiltersClone.push((subFilter as any).clone());
}
}
const clone = new QQueryFilter(
criteriaClone,
orderBysClone,
subFiltersClone,
this.booleanOperator,
this.skip,
this.limit
);
return (clone);
}
}
+193
View File
@@ -0,0 +1,193 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. 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/>.
*/
import {QTableMetaData} from "../../../src/model/metaData/QTableMetaData";
describe("QTableMetaData tests", () =>
{
it("it should clone", () =>
{
/////////////////////////////////////
// build a table with lots of data //
/////////////////////////////////////
const table = new QTableMetaData(
{
"name": "myTable",
"label": "My Table",
"isHidden": false,
"primaryKeyField": "id",
"icon": {"name": "link"},
"fields": {
"id": {
"name": "id",
"label": "Id",
"type": "INTEGER",
"isRequired": false,
"isEditable": true,
"isHeavy": false,
"isHidden": false,
"gridColumns": -1,
"displayFormat": "%s"
},
"name": {
"name": "name",
"label": "Name",
"type": "STRING",
"isRequired": false,
"isEditable": true,
"isHeavy": false,
"isHidden": false,
"gridColumns": -1,
"displayFormat": "%s",
"maxLength": 100
},
"clientId": {
"name": "clientId",
"label": "Client",
"type": "INTEGER",
"isRequired": false,
"isEditable": true,
"isHeavy": false,
"isHidden": false,
"gridColumns": -1,
"possibleValueSourceName": "client",
"displayFormat": "%s",
"adornments": [
{
"type": "LINK",
"values": {
"toRecordFromTable": "client"
}
}
]
},
"apiKey": {
"name": "apiKey",
"label": "API Key",
"type": "PASSWORD",
"isRequired": false,
"isEditable": true,
"isHeavy": false,
"isHidden": false,
"gridColumns": -1,
"displayFormat": "%s",
"adornments": [
{
"type": "REVEAL"
}
]
},
"type": {
"name": "type",
"label": "Type",
"type": "BOOLEAN",
"isRequired": false,
"isEditable": true,
"isHeavy": false,
"isHidden": false,
"gridColumns": -1,
"displayFormat": "%s",
"possibleValueSourceName": "type",
"supplementalFieldMetaData": {
"materialDashboard": {
"formAdjusterIdentifier": "table:myTable;field:type",
"onChangeFormAdjuster": {
"name": "com.qqq.MyTableFormAdjuster",
"codeType": "JAVA"
},
"type": "materialDashboard"
}
}
},
},
"sections": [
{
"name": "identity",
"label": "Identity",
"tier": "T1",
"fieldNames": ["id", "name"],
"icon": {"name": "badge"},
"isHidden": false
},
{
"name": "authentication",
"label": "Authentication",
"tier": "T2",
"fieldNames": ["apiKey",],
"icon": {"name": "edit_attributes"},
"isHidden": false
},
],
"exposedJoins": [],
"supplementalTableMetaData": {
"materialDashboard": {
"onLoadFormAdjuster": {
"name": "com.qqq.MyTableFormAdjuster",
"codeType": "JAVA"
},
"type": "materialDashboard"
}
},
"capabilities": [
"TABLE_COUNT",
"TABLE_GET",
"TABLE_QUERY",
"QUERY_STATS",
"TABLE_INSERT",
"TABLE_UPDATE",
"TABLE_DELETE",
"TABLE_EXPORT"
],
"readPermission": true,
"insertPermission": true,
"editPermission": true,
"deletePermission": true,
"usesVariants": false,
"iconName": "link"
});
////////////////////////////////////////////////////////////////
// clone it - make a few assertions about data being the same //
////////////////////////////////////////////////////////////////
const cloneTable = table.clone();
expect(cloneTable.name).toEqual(table.name);
expect(cloneTable.sections?.length).toEqual(2);
expect(cloneTable.sections?.length).toEqual(table.sections?.length);
expect(cloneTable.sections?.[0].name).toEqual(table.sections?.[0].name);
///////////////////////////////////////////////////////////////////////////////////////////
// change some things in the clone - assert that they are then not equal to the original //
///////////////////////////////////////////////////////////////////////////////////////////
cloneTable.name = "changed"
expect(cloneTable.name).not.toEqual(table.name);
if(cloneTable.sections)
{
cloneTable.sections[0].name = "changed"
cloneTable.sections[0].isHidden = true;
}
expect(cloneTable.sections?.[0].name).not.toEqual(table.sections?.[0].name);
expect(cloneTable.sections?.[0].isHidden).not.toEqual(table.sections?.[0].isHidden);
});
});