refact: convert to TS

This commit is contained in:
joshuashoemaker 2022-06-19 18:41:21 -05:00
parent 2179a90562
commit 3939d560d0
47 changed files with 14195 additions and 255 deletions

View File

@ -1,13 +0,0 @@
{
"presets": [
"@babel/preset-env"
],
"plugins": [
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
]
]
}

File diff suppressed because one or more lines are too long

8
lib/constants/filterTypes.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
declare const filterTypes: {
EQUAL: string;
GREATER: string;
GREATEREQUAL: string;
LESSER: string;
LESSEREQUAL: string;
};
export default filterTypes;

View File

@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const filterTypes = {
EQUAL: 'EQUAL',
GREATER: 'GREATER',
GREATEREQUAL: 'GREATEREQUAL',
LESSER: 'LESSER',
LESSEREQUAL: 'LESSEREQUAL',
};
exports.default = filterTypes;

24
lib/entities/Nodule.d.ts vendored Normal file
View File

@ -0,0 +1,24 @@
import { noduleConstructorProps } from '../types/noduleTypes';
import { tableProps, tableRows } from '../types/tableTypes';
import Table from './Table';
declare abstract class Nodule {
id: string;
label: string;
type: 'Nodule';
isValid: boolean;
tables: Table[];
constructor(props: noduleConstructorProps);
abstract export(): tableRows;
asTable: () => Table | null;
getProperties: () => {
id: string;
label: string;
type: "Nodule";
tables: tableProps[];
isValid: boolean;
};
setTables: (tablesToSet: Table[]) => void;
private validateTables;
private validateConstructionProps;
}
export default Nodule;

120
lib/entities/Nodule.js Normal file
View File

@ -0,0 +1,120 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Table_1 = require("./Table");
class Nodule {
id;
label;
type;
isValid;
tables = [];
constructor(props) {
const validatePropsResponse = this.validateConstructionProps(props);
if (validatePropsResponse.status === 'ERR')
throw validatePropsResponse;
else {
this.id = props.id;
this.label = props.label;
this.type = 'Nodule';
this.isValid = true;
if (props.tables)
this.setTables(props.tables);
}
}
asTable = () => {
if (!this.export)
return null;
try {
return new Table_1.default({
id: this.id,
label: this.label,
rows: this.export()
});
}
catch (err) {
throw err;
}
};
getProperties = () => {
let tables = [];
if (!Array.isArray(this.tables))
tables = [];
else if (this.tables.length === 0)
tables = [];
else
tables = this.tables.map((t) => {
return t.getProperties();
});
const properties = {
id: this.id,
label: this.label,
type: this.type,
tables: tables,
isValid: this.isValid
};
return properties;
};
setTables = (tablesToSet) => {
const validateTablesResponse = this.validateTables(tablesToSet);
if (validateTablesResponse.status === 'ERR') {
throw validateTablesResponse;
}
else {
let tables = [];
if (!Array.isArray(tablesToSet))
tables = [tablesToSet];
else
tables = tablesToSet;
this.tables = tables;
}
};
validateTables = (tablesToImport) => {
const err = {
status: 'ERR',
error: {
label: 'Not all imported Tables are valid',
messages: []
}
};
let tables = [];
if (!tablesToImport) {
err.error.messages.push('No Tables imported');
return err;
}
else if (!Array.isArray(tablesToImport)) {
tables = [tablesToImport];
}
else
tables = tablesToImport;
for (let t = 0; t < tables.length; t++) {
if (!tables[t].isValid) {
err.error.messages.push(`Table[${t}] is not valid`);
}
}
if (err.error.messages.length === 0) {
return { status: 'OK' };
}
else {
return err;
}
};
validateConstructionProps = (props) => {
const err = {
status: 'ERR',
error: {
label: 'Error Creating Node',
messages: []
}
};
if (!props.id)
err.error.messages.push('No id on creation of Node');
if (!props.label)
err.error.messages.push('No label on creation of Node');
if (err.error.messages.length === 0) {
return { status: 'OK' };
}
else {
return err;
}
};
}
exports.default = Nodule;

16
lib/entities/Table.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
import { tableConstructorProps, tableProps, tableRows } from "../types/tableTypes";
declare class Table {
id: string;
label: string;
rows: tableRows;
type: 'Table';
isValid: boolean;
constructor(props: tableConstructorProps);
getProperties: () => tableProps;
get headers(): string[];
export: () => tableRows;
setRows: (rows: tableRows) => void;
private validateConstructionProps;
private validateRows;
}
export default Table;

103
lib/entities/Table.js Normal file
View File

@ -0,0 +1,103 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class Table {
id;
label;
rows = [];
type;
isValid;
constructor(props) {
const validatePropsResponse = this.validateConstructionProps(props);
if (validatePropsResponse.status === 'ERR')
throw validatePropsResponse;
else {
this.id = props.id;
this.label = props.label;
this.type = 'Table';
this.isValid = true;
if (props.rows)
this.setRows(props.rows);
}
}
getProperties = () => {
return {
id: this.id,
label: this.label,
rows: this.rows,
headers: this.headers,
type: this.type,
isValid: this.isValid
};
};
get headers() {
const rows = this.rows;
if (!Array.isArray(rows) || rows.length < 1)
return [];
const length = rows.length;
let lengthToSlice = 49;
if (length < 50)
lengthToSlice = length;
const firstSliceOfRows = rows.slice(0, lengthToSlice);
const headersOfSplicedRows = firstSliceOfRows.map(r => Object.keys(r));
const flatenedHeaders = headersOfSplicedRows.flat();
const uniqueHeaders = Array.from(new Set(flatenedHeaders));
return uniqueHeaders;
}
export = () => this.rows;
setRows = (rows) => {
const rowsValidation = this.validateRows(rows);
if (rowsValidation.status === 'ERR')
throw rowsValidation;
if (!Array.isArray(rows))
this.rows = [rows];
else
this.rows = rows;
};
validateConstructionProps = (props) => {
const err = {
status: 'ERR',
error: {
label: 'Error Creating Table',
messages: []
}
};
if (!props) {
err.error.messages.push('No props on creation of Table');
return err;
}
if (!props.id)
err.error.messages.push('No id on creation of Table');
if (!props.label)
err.error.messages.push('No label on creation of Table');
if (err.error.messages.length === 0)
return { status: 'OK' };
else
return err;
};
validateRows = (rowsToImport) => {
const err = {
status: 'ERR',
error: {
label: 'Error Creating Table',
messages: []
}
};
let rows = [];
if (!Array.isArray(rowsToImport))
rows = [rowsToImport];
else
rows = rowsToImport;
if (rows.length === 0)
err.error.messages.push('No Tables imported');
for (let r = 0; r < rows.length; r++) {
if (typeof rows[r] !== 'object') {
err.error.messages.push(`row[${r}] is not an object`);
}
}
if (err.error.messages.length > 0)
return err;
else
return { status: 'OK' };
};
}
exports.default = Table;

14
lib/entities/nodules/FilterNodule.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import Nodule from '../Nodule';
import { filterNoduleConstructionProps, filterParams, filterType } from '../../types/noduleTypes';
declare class FilterNodule extends Nodule {
filterType?: filterType;
filterParams: filterParams;
constructor(props: filterNoduleConstructionProps);
addFilter: (params: filterParams) => void;
setFilterType: (filterType: filterType) => void;
export: () => import("../../types/tableTypes").tableRow[];
private createFilterMethods;
private validateFilters;
private validateType;
}
export default FilterNodule;

View File

@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Nodule_1 = require("../Nodule");
const filterTypes_1 = require("../../constants/filterTypes");
class FilterNodule extends Nodule_1.default {
filterType;
filterParams;
constructor(props) {
super(props);
this.filterParams = props.filterParams;
if (props.filterType)
this.setFilterType(props.filterType);
}
addFilter = (params) => {
const filterValidation = this.validateFilters(params);
if (filterValidation.status === 'ERR')
throw filterValidation;
else
this.filterParams = { ...this.filterParams, ...params };
};
setFilterType = (filterType) => {
const typeValidation = this.validateType(filterType);
if (typeValidation.status === 'ERR')
throw typeValidation;
else
this.filterType = filterType;
};
export = () => {
let rows = this.tables.map(t => t.export()).flat();
let filters = this.createFilterMethods();
filters.forEach((f) => {
rows = rows.filter(f);
});
return rows;
};
createFilterMethods = () => {
const typeValidation = this.validateType(this.filterType);
if (typeValidation.status !== 'OK')
throw typeValidation;
let filters = [];
for (let key in this.filterParams) {
let filterMethod = () => { };
if (this.filterType === filterTypes_1.default.EQUAL)
filterMethod = (t) => t[key] === this.filterParams[key];
else if (this.filterType === filterTypes_1.default.GREATER)
filterMethod = (t) => t[key] > this.filterParams[key];
else if (this.filterType === filterTypes_1.default.GREATEREQUAL)
filterMethod = (t) => t[key] >= this.filterParams[key];
else if (this.filterType === filterTypes_1.default.LESSER)
filterMethod = (t) => t[key] < this.filterParams[key];
else if (this.filterType === filterTypes_1.default.LESSEREQUAL)
filterMethod = (t) => t[key] <= this.filterParams[key];
filters.push(filterMethod);
}
return filters;
};
validateFilters = (params) => {
const err = {
status: 'ERR',
error: {
label: 'Filter Parameter are not valid',
messages: []
}
};
if (typeof params !== 'object') {
const paramsType = typeof params;
err.error.messages.push(`Filter was of type ${paramsType} should be an object`);
}
if (err.error.messages.length > 0)
return err;
else
return { status: 'OK' };
};
validateType = (type) => {
const err = {
status: 'ERR',
error: {
label: 'Filter Type is not valid',
messages: []
}
};
if (!type)
err.error.messages.push(`Type must be one of: ${Object.keys(filterTypes_1.default)}`);
else if (Object.values(filterTypes_1.default).indexOf(type) < 0) {
err.error.messages.push(`Type must be one of: ${Object.keys(filterTypes_1.default)}`);
}
if (err.error.messages.length > 0)
return err;
else
return { status: 'OK' };
};
}
exports.default = FilterNodule;

14
lib/entities/nodules/GroupByNodule.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import { groupByNoduleConstructorProps, groupedByRows } from '../../types/noduleTypes';
import Nodule from '../Nodule';
import Table from '../Table';
declare class GroupByNodule extends Nodule {
groupByValue: string;
constructor(props: groupByNoduleConstructorProps);
asTables: () => Table[];
asTable: () => never;
export: () => never;
exportTables: () => groupedByRows;
setGroupByValue: (value: string) => void;
private validateGroupByValue;
}
export default GroupByNodule;

View File

@ -0,0 +1,68 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Nodule_1 = require("../Nodule");
const Table_1 = require("../Table");
class GroupByNodule extends Nodule_1.default {
groupByValue = '';
constructor(props) {
super(props);
if (props.groupByValue)
this.setGroupByValue(props.groupByValue);
}
asTables = () => {
const exports = this.exportTables();
const tables = [];
for (let key in exports) {
const newTableProps = {
id: `${this.id}-${key}`,
label: `${this.label} by ${key}`,
rows: exports[key]
};
const table = new Table_1.default(newTableProps);
tables.push(table);
}
return tables;
};
asTable = () => {
throw new Error('"asTable()" can not be called by GroupByNodule. Call "asTables()"');
};
export = () => {
throw new Error('"export()" can not be called by GroupByNodule. Call "exportTables()"');
};
exportTables = () => {
const { groupByValue } = this;
const rows = this.tables.map(t => t.export()).flat();
const groupedByRows = rows.reduce((groups, r) => {
const val = r[groupByValue];
groups[val] = groups[val] || [];
groups[val].push(r);
return groups;
}, {});
return groupedByRows;
};
setGroupByValue = (value) => {
const valueValidation = this.validateGroupByValue(value);
if (valueValidation.status === 'ERR')
throw valueValidation;
else
this.groupByValue = value;
};
validateGroupByValue = (value) => {
const err = {
status: 'ERR',
error: {
label: 'Filter Parameter are not valid',
messages: []
}
};
if (typeof value !== 'string') {
const valueType = typeof value;
err.error.messages.push(`GroupBy value was of type ${valueType}, should be a string`);
}
if (err.error.messages.length > 0)
return err;
else
return { status: 'OK' };
};
}
exports.default = GroupByNodule;

13
lib/entities/nodules/JoinNodule.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import { joinBy, joinNoduleConstructionProps, joinParam } from '../../types/noduleTypes';
import Nodule from '../Nodule';
declare class JoinNodule extends Nodule {
baseTableLabel: string;
joinParams: joinParam[];
constructor(props: joinNoduleConstructionProps);
export: () => {
[x: string]: unknown;
}[];
setJoinBy: (joinBy: joinBy) => void;
private validateJoinBy;
}
export default JoinNodule;

View File

@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Nodule_1 = require("../Nodule");
class JoinNodule extends Nodule_1.default {
baseTableLabel = '';
joinParams = [];
constructor(props) {
super(props);
if (props.joinBy)
this.setJoinBy(props.joinBy);
}
export = () => {
const baseTable = this.tables.find(t => t.label === this.baseTableLabel);
if (!baseTable)
return [];
const baseTableRows = baseTable.export();
const tablesToJoin = this.tables.filter(t => {
return t.label !== this.baseTableLabel;
});
const relatedTables = this.joinParams.map(joinParam => {
const foreignTable = tablesToJoin.find(t => t.label === joinParam.foreignTable);
if (!foreignTable)
return [];
const foreignTableRows = foreignTable.export();
const mergedRows = baseTableRows.map(baseRow => {
const matchingForeignRow = foreignTableRows.find(foreignRow => {
return baseRow[joinParam.primaryTableKey] === foreignRow[joinParam.matchingKey];
});
let rowToMerge = {};
for (let key in matchingForeignRow) {
rowToMerge[`${joinParam.foreignTable}::${key}`] = matchingForeignRow[key];
}
return { ...baseRow, ...rowToMerge };
});
return mergedRows;
});
return relatedTables[0];
};
setJoinBy = (joinBy) => {
const joinByValidation = this.validateJoinBy(joinBy);
if (joinByValidation.status === 'ERR')
throw joinByValidation;
else {
this.baseTableLabel = joinBy.baseTableLabel;
this.joinParams = joinBy.joinParams;
}
};
validateJoinBy = (joinBy) => {
const err = {
status: 'ERR',
error: {
label: 'JoinBy Parameters are not valid',
messages: []
}
};
const { baseTableLabel, joinParams } = joinBy;
if (!baseTableLabel)
err.error.messages.push('No baseTableLabel provided');
if (!Array.isArray(joinParams)) {
const joinParamsType = typeof joinParams;
err.error.messages.push(`Keys was of type ${joinParamsType} should be an array`);
return err;
}
for (let p = 0; p < joinParams.length; p++) {
if (typeof joinParams[p] !== 'object')
err.error.messages.push(`joinParams[${p}] is not an object`);
}
if (err.error.messages.length > 0)
return err;
else
return { status: 'OK' };
};
}
exports.default = JoinNodule;

View File

@ -0,0 +1,11 @@
import { transformNoduleConstructionProps, transformStruct } from '../../types/noduleTypes';
import { tableRow } from '../../types/tableTypes';
import Nodule from '../Nodule';
declare class TransformNodule extends Nodule {
structure: transformStruct;
constructor(props: transformNoduleConstructionProps);
export: () => tableRow[];
setStructure: (struct: transformStruct) => void;
private validateStructureProps;
}
export default TransformNodule;

View File

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Nodule_1 = require("../Nodule");
class TransformNodule extends Nodule_1.default {
structure = {};
constructor(props) {
super(props);
if (props.structure)
this.setStructure(props.structure);
}
export = () => {
const rows = this.tables.map(t => t.export()).flat();
const transformedRows = rows.map(r => {
let mapShape = {};
for (const [key, value] of Object.entries(this.structure)) {
mapShape[value] = r[key];
}
return mapShape;
});
return transformedRows;
};
setStructure = (struct) => {
const structureValidation = this.validateStructureProps(struct);
if (structureValidation.status === 'ERR')
throw structureValidation;
else
this.structure = struct;
};
validateStructureProps = (struct) => {
const err = {
status: 'ERR',
error: {
label: 'Ptructure Parameters are not valid',
messages: []
}
};
if (!struct) {
err.error.messages.push('No structure provided');
return err;
}
for (let key in struct) {
if (typeof struct[key] !== 'string')
err.error.messages.push(`Key [${struct}] is not a String`);
}
if (err.error.messages.length > 0)
return err;
else
return { status: 'OK' };
};
}
exports.default = TransformNodule;

16
lib/index.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
import Table from './entities/Table';
import Nodule from './entities/Nodule';
import FilterNodule from './entities/nodules/FilterNodule';
import JoinNodule from './entities/nodules/JoinNodule.js';
import TransformNodule from './entities/nodules/TransformNodule.js';
import GroupByNodule from './entities/nodules/GroupByNodule.js';
export { Table, Nodule, FilterNodule, JoinNodule, TransformNodule, GroupByNodule };
declare const _default: {
Table: typeof Table;
Nodule: typeof Nodule;
FilterNodule: typeof FilterNodule;
JoinNodule: typeof JoinNodule;
TransformNodule: typeof TransformNodule;
GroupByNodule: typeof GroupByNodule;
};
export default _default;

23
lib/index.js Normal file
View File

@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GroupByNodule = exports.TransformNodule = exports.JoinNodule = exports.FilterNodule = exports.Nodule = exports.Table = void 0;
const Table_1 = require("./entities/Table");
exports.Table = Table_1.default;
const Nodule_1 = require("./entities/Nodule");
exports.Nodule = Nodule_1.default;
const FilterNodule_1 = require("./entities/nodules/FilterNodule");
exports.FilterNodule = FilterNodule_1.default;
const JoinNodule_js_1 = require("./entities/nodules/JoinNodule.js");
exports.JoinNodule = JoinNodule_js_1.default;
const TransformNodule_js_1 = require("./entities/nodules/TransformNodule.js");
exports.TransformNodule = TransformNodule_js_1.default;
const GroupByNodule_js_1 = require("./entities/nodules/GroupByNodule.js");
exports.GroupByNodule = GroupByNodule_js_1.default;
exports.default = {
Table: Table_1.default,
Nodule: Nodule_1.default,
FilterNodule: FilterNodule_1.default,
JoinNodule: JoinNodule_js_1.default,
TransformNodule: TransformNodule_js_1.default,
GroupByNodule: GroupByNodule_js_1.default
};

7
lib/types/errType.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
export declare type errType = {
status: 'ERR' | 'OK';
error: {
label: string;
messages: string[];
};
};

2
lib/types/errType.js Normal file
View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

36
lib/types/noduleTypes.d.ts vendored Normal file
View File

@ -0,0 +1,36 @@
import Table from "../entities/Table";
import { tableRow } from "./tableTypes";
declare type noduleConstructorProps = {
id: string;
label: string;
type?: 'Nodule';
tables?: Table[];
};
declare type filterType = 'EQUAL' | 'GREATER' | 'GREATEREQUAL' | 'LESSER' | 'LESSEREQUAL';
declare type filterParams = Record<string, string | number>;
declare type filterNoduleConstructionProps = noduleConstructorProps & {
filterType: filterType;
} & {
filterParams: filterParams;
};
declare type joinParam = {
foreignTable: string;
primaryTableKey: string;
matchingKey: string;
};
declare type joinBy = {
baseTableLabel: string;
joinParams: joinParam[];
};
declare type joinNoduleConstructionProps = noduleConstructorProps & {
joinBy: joinBy;
};
declare type transformStruct = Record<string, string>;
declare type transformNoduleConstructionProps = noduleConstructorProps & {
structure: transformStruct;
};
declare type groupByNoduleConstructorProps = noduleConstructorProps & {
groupByValue: string;
};
declare type groupedByRows = Record<string, tableRow[]>;
export { noduleConstructorProps, filterNoduleConstructionProps, filterType, filterParams, joinParam, joinBy, joinNoduleConstructionProps, transformStruct, transformNoduleConstructionProps, groupByNoduleConstructorProps, groupedByRows };

2
lib/types/noduleTypes.js Normal file
View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

17
lib/types/tableTypes.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
declare type tableConstructorProps = {
id: string;
label: string;
rows: tableRows;
type?: 'Table';
};
declare type tableRow = Record<string, unknown>;
declare type tableRows = tableRow[];
declare type tableProps = {
id: string;
label: string;
rows: tableRows;
headers: string[];
type: 'Table';
isValid: boolean;
};
export { tableConstructorProps, tableRow, tableRows, tableProps };

2
lib/types/tableTypes.js Normal file
View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

13080
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,15 @@
{
"name": "lovelacejs",
"version": "0.2.1",
"version": "0.2.2",
"description": "Lovelace.js is a modern JavaScript Library to create objects that easily mutate data through relationships, filtering, and tranforming the shape of data.",
"main": "index.js",
"directories": {
"test": "tests"
},
"scripts": {
"build": "webpack --mode production",
"tests": "node ./tests/index.js"
"build": "tsc",
"lint": "tslint -p tsconfig.json",
"tests": "ts-node ./tests/index.ts"
},
"repository": {
"type": "git",
@ -25,6 +26,9 @@
"@babel/plugin-proposal-class-properties": "^7.10.1",
"@babel/preset-env": "^7.10.3",
"babel-loader": "^8.1.0",
"ts-node": "^10.8.1",
"tslint": "^6.1.3",
"typescript": "^4.7.4",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12"
}

View File

@ -1,16 +1,30 @@
import Table from './Table.js'
import { errType } from '../types/errType'
import { noduleConstructorProps } from '../types/noduleTypes'
import { tableProps, tableRows } from '../types/tableTypes'
import Table from './Table'
class Nodule {
constructor (props) {
const validatePropsResponse = this._validateConstructionProps(props)
if (validatePropsResponse.status === 'ERR') {
throw validatePropsResponse
}
abstract class Nodule {
id: string
label: string
type: 'Nodule'
isValid: boolean
tables: Table[] = []
constructor (props: noduleConstructorProps) {
const validatePropsResponse = this.validateConstructionProps(props)
if (validatePropsResponse.status === 'ERR') throw validatePropsResponse
else {
this._assignProps(props)
this.id = props.id
this.label = props.label
this.type = 'Nodule'
this.isValid = true
if (props.tables) this.setTables(props.tables)
}
}
abstract export () : tableRows
asTable = () => {
if (!this.export) return null
try {
@ -25,10 +39,10 @@ class Nodule {
}
getProperties = () => {
let tables = []
let tables: tableProps[] = []
if (!Array.isArray(this.tables)) tables = []
else if (this.tables.length === 0) tables = []
else tables = this.tables.map(t => {
else tables = this.tables.map((t: Table) => {
return t.getProperties()
})
@ -43,35 +57,20 @@ class Nodule {
return properties
}
importTables = tablesToImport => {
console.log('Function importTables has been depricated, please use "setTables()"')
this.setTables(tablesToImport)
}
setTables = tablesToSet => {
const validateTablesResponse = this._validateTables(tablesToSet)
setTables = (tablesToSet: Table[]) => {
const validateTablesResponse = this.validateTables(tablesToSet)
if (validateTablesResponse.status === 'ERR') {
throw validateTablesResponse
} else {
let tables = []
let tables: Table[] = []
if (!Array.isArray(tablesToSet)) tables = [tablesToSet]
else tables = tablesToSet
this.tables = tables
}
}
_assignProps = props => {
this.id = props.id
this.label = props.label
this.type = 'Nodule'
this.isValid = true
if (props.tables) this.setTables(props.tables)
else this.tables = []
}
_validateTables = tablesToImport => {
const err = {
private validateTables = (tablesToImport: Table[]) => {
const err: errType = {
status: 'ERR',
error: {
label: 'Not all imported Tables are valid',
@ -100,8 +99,8 @@ class Nodule {
}
}
_validateConstructionProps = (props) => {
const err = {
private validateConstructionProps = (props: noduleConstructorProps) => {
const err: errType = {
status: 'ERR',
error: {
label: 'Error Creating Node',

View File

@ -1,11 +1,27 @@
import { errType } from "../types/errType"
import { tableConstructorProps, tableProps, tableRows } from "../types/tableTypes"
class Table {
constructor (props) {
const validatePropsResponse = this._validateConstructionProps(props)
id: string
label: string
rows: tableRows = []
type: 'Table'
isValid: boolean
constructor (props: tableConstructorProps) {
const validatePropsResponse = this.validateConstructionProps(props)
if (validatePropsResponse.status === 'ERR') throw validatePropsResponse
else this._assignProps(props)
else {
this.id = props.id
this.label = props.label
this.type = 'Table'
this.isValid = true
if (props.rows) this.setRows(props.rows)
}
}
getProperties = () => {
getProperties = (): tableProps => {
return {
id: this.id,
label: this.label,
@ -33,26 +49,16 @@ class Table {
export = () => this.rows
setRows = rows => {
const rowsValidation = this._validateRows(rows)
setRows = (rows: tableRows) => {
const rowsValidation = this.validateRows(rows)
if (rowsValidation.status === 'ERR') throw rowsValidation
if (!Array.isArray(rows)) this.rows = [rows]
else this.rows = rows
}
_assignProps = props => {
this.id = props.id
this.label = props.label
this.type = 'Table'
this.isValid = true
if (props.rows) this.setRows(props.rows)
else this.rows = []
}
_validateConstructionProps = props => {
const err = {
private validateConstructionProps = (props: tableConstructorProps) => {
const err: errType = {
status: 'ERR',
error: {
label: 'Error Creating Table',
@ -71,8 +77,8 @@ class Table {
else return err
}
_validateRows = rowsToImport => {
const err = {
private validateRows = (rowsToImport: tableRows) => {
const err: errType = {
status: 'ERR',
error: {
label: 'Error Creating Table',

View File

@ -1,65 +1,67 @@
import Nodule from '../Nodule.js'
import filterTypes from '../../constants/filterTypes.js'
import Nodule from '../Nodule'
import filterTypes from '../../constants/filterTypes'
import { filterNoduleConstructionProps, filterParams, filterType } from '../../types/noduleTypes'
import { errType } from '../../types/errType'
class FilterNodule extends Nodule {
constructor (props) {
filterType?: filterType
filterParams: filterParams
constructor (props: filterNoduleConstructionProps) {
super (props)
this._assignProps(props)
this.filterParams = props.filterParams
if (props.filterType) this.setFilterType(props.filterType)
}
addFilter = params => {
const filterValidation = this._validateFilters(params)
addFilter = (params: filterParams) => {
const filterValidation = this.validateFilters(params)
if (filterValidation.status === 'ERR') throw filterValidation
else this.filterParams = {...this.filterParams, ...params}
}
setFilterType = filterType => {
const typeValidation = this._validateType(filterType)
setFilterType = (filterType: filterType) => {
const typeValidation = this.validateType(filterType)
if (typeValidation.status === 'ERR') throw typeValidation
else this.filterType = filterType
}
export = () => {
let rows = this.tables.map(t => t.export() ).flat()
let filters = this._createFilterMethods()
let filters = this.createFilterMethods()
filters.forEach(f => {
filters.forEach((f: any) => { // TODO: I dont like the any, gotta find a type solution
rows = rows.filter(f)
})
return rows
}
_assignProps = props => {
this.filterParams = props.filterParams || {}
if (props.filterType) this.setFilterType(props.filterType)
}
_createFilterMethods = () => {
const typeValidation = this._validateType(this.filterType)
private createFilterMethods = (): Function[] => {
const typeValidation = this.validateType(this.filterType)
if (typeValidation.status !== 'OK') throw typeValidation
let filters = []
let filters: Function[] = []
for (let key in this.filterParams) {
let filterMethod = {}
let filterMethod: Function = () => {}
if (this.filterType === filterTypes.EQUAL)
filterMethod = t => t[key] === this.filterParams[key]
filterMethod = (t: filterParams) => t[key] === this.filterParams[key]
else if (this.filterType === filterTypes.GREATER)
filterMethod = t => t[key] > this.filterParams[key]
filterMethod = (t: filterParams) => t[key] > this.filterParams[key]
else if (this.filterType === filterTypes.GREATEREQUAL)
filterMethod = t => t[key] >= this.filterParams[key]
filterMethod = (t: filterParams) => t[key] >= this.filterParams[key]
else if (this.filterType === filterTypes.LESSER)
filterMethod = t => t[key] < this.filterParams[key]
filterMethod = (t: filterParams) => t[key] < this.filterParams[key]
else if (this.filterType === filterTypes.LESSEREQUAL)
filterMethod = t => t[key] <= this.filterParams[key]
filterMethod = (t: filterParams) => t[key] <= this.filterParams[key]
filters.push(filterMethod)
}
return filters
}
_validateFilters = params => {
const err = {
private validateFilters = (params: filterParams) => {
const err: errType = {
status: 'ERR',
error: {
label: 'Filter Parameter are not valid',
@ -76,8 +78,8 @@ class FilterNodule extends Nodule {
else return { status: 'OK' }
}
_validateType = type => {
const err = {
private validateType = (type?: filterType) => {
const err: errType = {
status: 'ERR',
error: {
label: 'Filter Type is not valid',
@ -85,7 +87,8 @@ class FilterNodule extends Nodule {
}
}
if (Object.values(filterTypes).indexOf(type) < 0) {
if(!type) err.error.messages.push(`Type must be one of: ${Object.keys(filterTypes)}`)
else if (Object.values(filterTypes).indexOf(type) < 0) {
err.error.messages.push(`Type must be one of: ${Object.keys(filterTypes)}`)
}

View File

@ -1,65 +0,0 @@
import Nodule from '../Nodule.js'
import Table from '../Table.js'
class GroupByNodule extends Nodule {
constructor (props) {
super (props)
this._assignProps(props)
}
/* Overload the Nodule Method
Returns an Array of Tables with modified ids */
asTable = () => {
const exports = this.export()
const tables = []
for (let key in exports) {
const newTableProps = {
id: `${this.id}-${key}`,
label: `${this.label} by ${key}`,
rows: exports[key]
}
const table = new Table(newTableProps)
tables.push(table)
}
return tables
}
export = () => {
const { groupByValue } = this
const rows = this.tables.map(t => t.export() ).flat()
const groupedByRows = rows.reduce((groups, r) => {
const val = r[groupByValue]
groups[val] = groups[val] || []
groups[val].push(r)
return groups
}, {})
return groupedByRows
}
setGroupByValue = value => {
const valueValidation = this._validateGroupByValue(value)
if(valueValidation.status === 'ERR') throw valueValidation
else this.groupByValue = value
}
_assignProps = props => {
if (props.groupByValue) this.setGroupByValue(props.groupByValue)
}
_validateGroupByValue = value => {
const err = {
status: 'ERR',
error: {
label: 'Filter Parameter are not valid',
messages: []
}
}
if (typeof value !== 'string') {
const valueType = typeof value
err.error.messages.push(`GroupBy value was of type ${valueType}, should be a string`)
} else return { status: 'OK' }
}
}
export default GroupByNodule

View File

@ -0,0 +1,75 @@
import { errType } from '../../types/errType'
import { groupByNoduleConstructorProps, groupedByRows } from '../../types/noduleTypes'
import { tableConstructorProps } from '../../types/tableTypes'
import Nodule from '../Nodule'
import Table from '../Table'
class GroupByNodule extends Nodule {
groupByValue: string = ''
constructor (props: groupByNoduleConstructorProps) {
super (props)
if (props.groupByValue) this.setGroupByValue(props.groupByValue)
}
asTables = (): Table[] => {
const exports = this.exportTables()
const tables = []
for (let key in exports) {
const newTableProps: tableConstructorProps = {
id: `${this.id}-${key}`,
label: `${this.label} by ${key}`,
rows: exports[key]
}
const table = new Table(newTableProps)
tables.push(table)
}
return tables
}
asTable = () => {
throw new Error('"asTable()" can not be called by GroupByNodule. Call "asTables()"')
}
export = () => {
throw new Error('"export()" can not be called by GroupByNodule. Call "exportTables()"')
}
exportTables = (): groupedByRows => {
const { groupByValue } = this
const rows = this.tables.map(t => t.export() ).flat()
const groupedByRows = rows.reduce((groups: groupedByRows, r) => {
const val: string = r[groupByValue] as string
groups[val] = groups[val] || []
groups[val].push(r)
return groups
}, {})
return groupedByRows
}
setGroupByValue = (value: string) => {
const valueValidation = this.validateGroupByValue(value)
if(valueValidation.status === 'ERR') throw valueValidation
else this.groupByValue = value
}
private validateGroupByValue = (value: string) => {
const err: errType = {
status: 'ERR',
error: {
label: 'Filter Parameter are not valid',
messages: []
}
}
if (typeof value !== 'string') {
const valueType = typeof value
err.error.messages.push(`GroupBy value was of type ${valueType}, should be a string`)
}
if (err.error.messages.length > 0) return err
else return { status: 'OK' }
}
}
export default GroupByNodule

View File

@ -1,29 +1,37 @@
import Nodule from '../Nodule.js'
import { errType } from '../../types/errType'
import { joinBy, joinNoduleConstructionProps, joinParam } from '../../types/noduleTypes'
import { tableRow } from '../../types/tableTypes'
import Nodule from '../Nodule'
class JoinNodule extends Nodule {
constructor (props) {
baseTableLabel: string = ''
joinParams: joinParam[] = []
constructor (props: joinNoduleConstructionProps) {
super(props)
this._assignProps(props)
if (props.joinBy) this.setJoinBy(props.joinBy)
}
export = () => {
const baseTable = this.tables.find(t => t.label === this.baseTableLabel)
if (!baseTable) return []
const baseTableRows = baseTable.export()
const tablesToJoin = this.tables.filter(t => {
return t.label !== this.baseTableLabel
})
const relatedTables = this.joinParams.map(joinParam => {
const foreignTable = tablesToJoin.find(t => {
return t.label === joinParam.foreignTable
})
const foreignTable = tablesToJoin.find(t => t.label === joinParam.foreignTable)
if (!foreignTable) return []
const foreignTableRows = foreignTable.export()
const mergedRows = baseTableRows.map(baseRow => {
const matchingForeignRow = foreignTableRows.find(foreignRow => {
return baseRow[joinParam.primaryTableKey] === foreignRow[joinParam.matchingKey]
})
let rowToMerge = {}
let rowToMerge: tableRow = {}
for (let key in matchingForeignRow) {
rowToMerge[`${joinParam.foreignTable}::${key}`] = matchingForeignRow[key]
}
@ -36,8 +44,8 @@ class JoinNodule extends Nodule {
return relatedTables[0]
}
setJoinBy = joinBy => {
const joinByValidation = this._validateJoinBy(joinBy)
setJoinBy = (joinBy: joinBy) => {
const joinByValidation = this.validateJoinBy(joinBy)
if (joinByValidation.status === 'ERR') throw joinByValidation
else {
this.baseTableLabel = joinBy.baseTableLabel
@ -45,12 +53,8 @@ class JoinNodule extends Nodule {
}
}
_assignProps = props => {
if (props.joinBy) this.setJoinBy(props.joinBy)
}
_validateJoinBy = joinBy => {
const err = {
private validateJoinBy = (joinBy: joinBy) => {
const err: errType = {
status: 'ERR',
error: {
label: 'JoinBy Parameters are not valid',

View File

@ -1,16 +1,21 @@
import Nodule from '../Nodule.js'
import { errType } from '../../types/errType'
import { transformNoduleConstructionProps, transformStruct } from '../../types/noduleTypes'
import { tableRow } from '../../types/tableTypes'
import Nodule from '../Nodule'
class TransformNodule extends Nodule {
constructor (props) {
structure: transformStruct = {}
constructor (props: transformNoduleConstructionProps) {
super(props)
this._assignProps(props)
if (props.structure) this.setStructure(props.structure)
}
export = () => {
const rows = this.tables.map(t => t.export()).flat()
const transformedRows = rows.map(r => {
let mapShape = {}
let mapShape: tableRow = {}
for (const [ key, value ] of Object.entries(this.structure)) {
mapShape[value] = r[key]
}
@ -19,18 +24,14 @@ class TransformNodule extends Nodule {
return transformedRows
}
setStructure = struct => {
const structureValidation = this._validateStructureProps(struct)
setStructure = (struct: transformStruct) => {
const structureValidation = this.validateStructureProps(struct)
if (structureValidation.status === 'ERR') throw structureValidation
else this.structure = struct
}
_assignProps = props => {
if (props.structure) this.setStructure(props.structure)
}
_validateStructureProps = struct => {
const err = {
private validateStructureProps = (struct: transformStruct) => {
const err: errType = {
status: 'ERR',
error: {
label: 'Ptructure Parameters are not valid',

View File

@ -1,6 +1,6 @@
import Table from './entities/Table.js'
import Nodule from './entities/Nodule.js'
import FilterNodule from './entities/nodules/FilterNodule.js'
import Table from './entities/Table'
import Nodule from './entities/Nodule'
import FilterNodule from './entities/nodules/FilterNodule'
import JoinNodule from './entities/nodules/JoinNodule.js'
import TransformNodule from './entities/nodules/TransformNodule.js'
import GroupByNodule from './entities/nodules/GroupByNodule.js'

7
src/types/errType.ts Normal file
View File

@ -0,0 +1,7 @@
export type errType = {
status: 'ERR' | 'OK',
error: {
label: string,
messages: string[]
}
}

38
src/types/noduleTypes.ts Normal file
View File

@ -0,0 +1,38 @@
import Table from "../entities/Table"
import { tableRow } from "./tableTypes"
type noduleConstructorProps = {
id: string,
label: string,
type?: 'Nodule'
tables?: Table[]
}
type filterType = 'EQUAL' | 'GREATER' | 'GREATEREQUAL' | 'LESSER' | 'LESSEREQUAL'
type filterParams = Record<string, string | number>
type filterNoduleConstructionProps = noduleConstructorProps & {filterType: filterType} & {filterParams: filterParams}
type joinParam = { foreignTable: string, primaryTableKey: string, matchingKey: string }
type joinBy = { baseTableLabel: string, joinParams: joinParam[] }
type joinNoduleConstructionProps = noduleConstructorProps & { joinBy: joinBy }
type transformStruct = Record<string, string>
type transformNoduleConstructionProps = noduleConstructorProps & { structure: transformStruct }
type groupByNoduleConstructorProps = noduleConstructorProps & { groupByValue: string }
type groupedByRows = Record<string, tableRow[]>
export {
noduleConstructorProps,
filterNoduleConstructionProps,
filterType,
filterParams,
joinParam,
joinBy,
joinNoduleConstructionProps,
transformStruct,
transformNoduleConstructionProps,
groupByNoduleConstructorProps,
groupedByRows
}

26
src/types/tableTypes.ts Normal file
View File

@ -0,0 +1,26 @@
type tableConstructorProps = {
id: string,
label: string,
rows: tableRows,
type?: 'Table',
}
type tableRow = Record<string, unknown>
type tableRows = tableRow[]
type tableProps = {
id: string,
label: string,
rows: tableRows,
headers: string[],
type: 'Table',
isValid: boolean
}
export {
tableConstructorProps,
tableRow,
tableRows,
tableProps
}

View File

@ -1,5 +1,5 @@
import Nodule from '../../src/entities/Nodule.js'
import Table from '../../src/entities/Table.js'
import Nodule from '../../src/entities/Nodule'
import Table from '../../src/entities/Table'
const input = {
id: 'ABC',
@ -65,44 +65,6 @@ const createNodeWithoutTables = () => {
}
}
const importTables = () => {
const table = new Table({
id: 'XYZ',
label: 'Test Table',
rows: [{ id: 'abc', data: 'row' }]
})
const expectedOutput = {
id: 'ABC',
label: 'Test Node',
type: 'Nodule',
tables: [{
id: 'XYZ',
label: 'Test Table',
rows: [{ id: 'abc', data: 'row' }],
headers: [ 'id', 'data' ],
type: 'Table',
isValid: true
}],
isValid: true
}
try {
const nodule = new Nodule({
id: 'ABC',
label: 'Test Node',
})
nodule.importTables(table)
const nodeProps = nodule.getProperties()
if (JSON.stringify(nodeProps) == JSON.stringify(expectedOutput)) return true
else return false
} catch (err) {
console.log(err)
return false
}
}
const failToExport = () => {
const expectedOutput = null
const nodule = new Nodule({
@ -163,7 +125,6 @@ const setTables = () => {
export default [
{ name: 'Entity | Get Nodule Properties', test: getNodeProperties },
{ name: 'Entity | Create Nodule Without Tables', test: createNodeWithoutTables },
{ name: 'Entity | Import Tables to Nodule', test: importTables },
{ name: 'Entity | Fail to Export', test: failToExport },
{ name: 'Entity | Nodule setTables', test: setTables }
]

View File

@ -1,5 +1,5 @@
import FilterNodule from '../../../src/entities/nodules/FilterNodule.js'
import Table from '../../../src/entities/Table.js'
import FilterNodule from '../../../src/entities/nodules/FilterNodule'
import Table from '../../../src/entities/Table'
const equalFilter = () => {
const expectedOutput = [

View File

@ -1,5 +1,5 @@
import GroupByNodule from '../../../src/entities/nodules/GroupByNodule.js'
import Table from '../../../src/entities/Table.js'
import GroupByNodule from '../../../src/entities/nodules/GroupByNodule'
import Table from '../../../src/entities/Table'
const groupByTest = () => {
const expectedOutput = {
@ -42,7 +42,7 @@ const groupByTest = () => {
return false
}
const groupedRows = groupByNodule.export()
const groupedRows = groupByNodule.exportTables()
if (JSON.stringify(groupedRows) === JSON.stringify(expectedOutput)) {
return true
} else {
@ -107,7 +107,7 @@ const groupByAsTables = () => {
}
// make checks here
const groupedTables = groupByNodule.asTable()
const groupedTables = groupByNodule.asTables()
const groupedTablesProps = groupedTables.map(t => t.getProperties())
if (JSON.stringify(groupedTablesProps) === JSON.stringify(expectedOutput)) {
return true
@ -116,8 +116,85 @@ const groupByAsTables = () => {
}
}
const groupByExportError = () => {
let table = {}
try {
table = new Table({
id: 'XYZ',
label: 'Test Table',
rows: [
{ id: 'abc', data: 'row', contractor: 'AshBritt' },
{ id: 'qwe', data: 'lh', contractor: 'AshBritt' },
]
})
} catch (err) {
return false
}
let groupByNodule = {}
try {
groupByNodule = new GroupByNodule({
id: 'ABC',
label: 'Test Group',
tables: [table],
groupByValue: 'contractor'
})
} catch (err) {
console.log(err)
return false
}
try {
groupByNodule.export()
return false
} catch (err) {
return true
}
}
const groupByAsTableError = () => {
let table = {}
try {
table = new Table({
id: 'XYZ',
label: 'Test Table',
rows: [
{ id: 'abc', data: 'row', contractor: 'AshBritt' },
{ id: 'qwe', data: 'lh', contractor: 'AshBritt' },
{ id: 'XYZ', data: 'row', contractor: 'AshBritt' },
{ id: 'XYZ', data: 'row', contractor: 'HeyDay' },
]
})
} catch (err) {
return false
}
let groupByNodule = {}
try {
groupByNodule = new GroupByNodule({
id: 'ABC',
label: 'Test Group',
tables: [table],
groupByValue: 'contractor'
})
} catch (err) {
console.log(err)
return false
}
try {
groupByNodule.asTable()
return false
} catch (err) {
return true
}
}
export default [
{ name: 'Entity | GroupBy Value', test: groupByTest },
{ name: 'Entity | GroupBy As Table', test: groupByAsTables },
{ name: 'Entity | GroupBy export() should error out', test: groupByExportError },
{ name: 'Entity | GroupBy asTable() should error out', test: groupByAsTableError },
]

View File

@ -1,5 +1,5 @@
import JoinNodule from '../../../src/entities/nodules/JoinNodule.js'
import Table from '../../../src/entities/Table.js'
import JoinNodule from '../../../src/entities/nodules/JoinNodule'
import Table from '../../../src/entities/Table'
const joinTables = () => {
const pickupTable = new Table({

View File

@ -1,5 +1,5 @@
import Table from '../../../lib/entities/Table.js'
import TransformNodule from '../../../lib/entities/nodules/TransformNodule.js'
import Table from '../../../src/entities/Table'
import TransformNodule from '../../../src/entities/nodules/TransformNodule'
const transformTable = () => {
const expectedOutput = [

View File

@ -1,4 +1,4 @@
import Table from '../../src/entities/Table.js'
import Table from '../../src/entities/Table'
const input = {
id: 'abc',

View File

@ -1,15 +1,17 @@
// import ProgressBar from 'progress'
import tableTests from '../tests/core/tableTests.js'
import noduleTests from '../tests/core/NoduleTests.js'
import filterNoduleTests from '../tests/core/nodules/filterNoduleTests.js'
import joinNoduleTests from '../tests/core/nodules/joinNoduleTests.js'
import transformNoduleTests from '../tests/core/nodules/transformNoduleTests.js'
import groupByNoduleTests from '../tests/core/nodules/groupByNoduleTests.js'
import tableTests from './core/tableTests.js'
import noduleTests from './core/NoduleTests.js'
import filterNoduleTests from './core/nodules/filterNoduleTests.js'
import joinNoduleTests from './core/nodules/joinNoduleTests.js'
import transformNoduleTests from './core/nodules/transformNoduleTests.js'
import groupByNoduleTests from './core/nodules/groupByNoduleTests.js'
function runTestsAndReturnFailures (tests) {
type unitTest = { name: string, test: Function }
function runTestsAndReturnFailures (tests: unitTest[]): string[] {
const testTotalCount = tests.length
const testsFailed = []
const testsFailed: string[] = []
for (let i = 0; i < testTotalCount; i++) {
const passedTest = tests[i].test()
@ -18,7 +20,7 @@ function runTestsAndReturnFailures (tests) {
return testsFailed
}
function init (tests) {
function init (tests: unitTest[]) {
const failedTestsResults = runTestsAndReturnFailures(tests)
if (failedTestsResults.length === 0) {
console.log('\x1b[32m%s\x1b[0m', 'All Tests Passed!!')

12
tsconfig.json Normal file
View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "commonjs",
"declaration": true,
"outDir": "./lib",
"strict": true,
"allowJs": true
},
"include": ["src", "tests"],
"exclude": ["node_modules", "tests"]
}

View File

@ -5,7 +5,7 @@ module.exports = {
output: {
path: path.resolve(__dirname),
filename: 'index.js',
library: 'dmein',
library: 'lovelace',
libraryTarget: 'umd',
globalObject: 'this',
umdNamedDefine: true