import {Grove, NestedTreeNodeData, NestedTreeNodeDataWithoutIds, TreeNodeData} from "./Grove"; import {cloneDeep} from "lodash"; import {addIdToComponent, generateIdForType, getIdTypeForTestable} from "./NodeHelpers"; import {TreeNode} from "./TreeNode"; import {InMemoryTestableNode} from "./InMemoryTestableNode"; import {Testables} from "./testables"; import {Offers} from "./Offers"; import {MemoryBranchNode} from "./MemoryBranchNode"; import {TestableNode, TestableNodeWithBranchAndFlow} from "./TestableNode"; import {BranchNode, StateBranchNode} from "./StateBranchNode"; import {TestableCompositeNode} from "./TestableCompositeNode"; /** * * * IDs ARE OPTIONAL, they will be generated if not provided * * { * id: 'tree-node-1', * testable: { * id: 'group-1', * type: 'AND', * mode: 'filter', * children: [ * { * id: 'context-1', * type: 'context', * mode: 'filter', * children: [ * { * id: 'testable-1', * type: 'InCategories', * options: { * expectedValues: ['123'], * inclusionType: 'forbidden' * }, * testableType: 'filter' * } * ] * } * ] * }, * branch: { * id: 'branch-1-offers', * type: 'offers', * children: [ * { * id: 'offer-1', * type: 'Discount', * options: { * discountValue: 10, * } * } * ] * } * } * * The result will be a TreeNode with in memory testable and branch objects */ export class TreeNodeFactory { // @ts-ignore private grove: Grove constructor( private readonly testables: Testables, private readonly offers: Offers, ) { } setGrove(grove: Grove): void { this.grove = grove; } createFromNestedData(nestedTreeNodeData: NestedTreeNodeData | NestedTreeNodeDataWithoutIds): TreeNode | undefined { const nestedTreeNodeDataWithIds = TreeNodeFactory.ensureIds(nestedTreeNodeData); return TreeNode.createTemporaryFromComponentsWithoutAncestors( new InMemoryTestableNode(nestedTreeNodeDataWithIds.testable, this.testables, nestedTreeNodeDataWithIds.parentBranchTestableId), new MemoryBranchNode(nestedTreeNodeDataWithIds.branch, this.offers, this.testables), this.grove, nestedTreeNodeDataWithIds.id, nestedTreeNodeData.extraData ); } // same as above but passing a json string // todo: test ts createFromNestedDataJson(json: string): TreeNode[] | undefined { const nestedNodesData = JSON.parse(json) as NestedTreeNodeData[]; return nestedNodesData.map(this.createFromNestedData.bind(this)).filter(v => v !== undefined) } /** * this should probably be in a different class */ createComponentsFromStateTreeNodeData(treeNodeData: TreeNodeData): { testableNode: TestableNodeWithBranchAndFlow, branch: BranchNode } { /** * Right now we're juust hard-coding this but this will have to change when we introduce tier branches and testable partials */ const testableNode = new TestableCompositeNode(this.testables, treeNodeData.testableId); const branch = new StateBranchNode(this.offers, treeNodeData.branch, this.testables); return { testableNode, branch } } public static ensureIds(nestedTreeNodeData: NestedTreeNodeData | NestedTreeNodeDataWithoutIds): NestedTreeNodeData { const newNestedTreeNodeData = cloneDeep(nestedTreeNodeData); // @ts-ignore newNestedTreeNodeData.id = newNestedTreeNodeData.id || generateIdForType('tree.node'); newNestedTreeNodeData.testable.id = newNestedTreeNodeData.testable.id || generateIdForType(getIdTypeForTestable(newNestedTreeNodeData.testable.type)); function addToTestableChild(child) { addIdToComponent(child) if (child.children) { child.children.forEach(addToTestableChild) } } // now the children newNestedTreeNodeData.testable?.children?.forEach?.(addToTestableChild) // now the branch newNestedTreeNodeData.branch.id = newNestedTreeNodeData.branch.id || generateIdForType('branch'); // now the children newNestedTreeNodeData.branch.children = newNestedTreeNodeData.branch.children.map((child) => { // now here's the thing, the child could be an offer or a tier branch if (nestedTreeNodeData.branch.type === 'tiered') { // tier is just a nested tree node so this will be recursive return TreeNodeFactory.ensureIds(child); } return { ...child, id: child.id || generateIdForType('offers'), } }) return newNestedTreeNodeData; } }