@onehat/data 1.17.0 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/cypress/plugins/index.js +2 -32
  2. package/cypress.config.js +42 -0
  3. package/package.json +22 -22
  4. package/src/Entity/Entity.js +328 -4
  5. package/src/Repository/Ajax.js +13 -3
  6. package/src/Repository/OneBuild.js +80 -0
  7. package/src/Repository/Repository.js +118 -30
  8. package/src/Schema/Schema.js +33 -0
  9. package/cypress/cypress.json +0 -177
  10. package/cypress.json +0 -1
  11. package/src/Entity/TreeNode.js +0 -190
  12. /package/cypress/{integration/Async.spec.js → e2e/Async.cy.js} +0 -0
  13. /package/cypress/{integration/Config.spec.js → e2e/Config.cy.js} +0 -0
  14. /package/cypress/{integration/Entity.spec.js → e2e/Entity.cy.js} +0 -0
  15. /package/cypress/{integration/OneHatData.spec.js → e2e/OneHatData.cy.js} +0 -0
  16. /package/cypress/{integration/Property/Base64.spec.js → e2e/Property/Base64.cy.js} +0 -0
  17. /package/cypress/{integration/Property/Boolean.spec.js → e2e/Property/Boolean.cy.js} +0 -0
  18. /package/cypress/{integration/Property/Currency.spec.js → e2e/Property/Currency.cy.js} +0 -0
  19. /package/cypress/{integration/Property/Date.spec.js → e2e/Property/Date.cy.js} +0 -0
  20. /package/cypress/{integration/Property/DateTime.spec.js → e2e/Property/DateTime.cy.js} +0 -0
  21. /package/cypress/{integration/Property/Float.spec.js → e2e/Property/Float.cy.js} +0 -0
  22. /package/cypress/{integration/Property/Integer.spec.js → e2e/Property/Integer.cy.js} +0 -0
  23. /package/cypress/{integration/Property/Json.spec.js → e2e/Property/Json.cy.js} +0 -0
  24. /package/cypress/{integration/Property/Percent.spec.js → e2e/Property/Percent.cy.js} +0 -0
  25. /package/cypress/{integration/Property/PercentInt.spec.js → e2e/Property/PercentInt.cy.js} +0 -0
  26. /package/cypress/{integration/Property/Property.spec.js → e2e/Property/Property.cy.js} +0 -0
  27. /package/cypress/{integration/Property/String.spec.js → e2e/Property/String.cy.js} +0 -0
  28. /package/cypress/{integration/Property/Time.spec.js → e2e/Property/Time.cy.js} +0 -0
  29. /package/cypress/{integration/Property/Uuid.spec.js → e2e/Property/Uuid.cy.js} +0 -0
  30. /package/cypress/{integration/Repository/Ajax.spec.js → e2e/Repository/Ajax.cy.js} +0 -0
  31. /package/cypress/{integration/Repository/LocalFromRemote.spec.js → e2e/Repository/LocalFromRemote.cy.js} +0 -0
  32. /package/cypress/{integration/Repository/Memory.spec.js → e2e/Repository/Memory.cy.js} +0 -0
  33. /package/cypress/{integration/Repository/OneBuild.spec.js → e2e/Repository/OneBuild.cy.js} +0 -0
  34. /package/cypress/{integration/Repository/Repository.spec.js → e2e/Repository/Repository.cy.js} +0 -0
  35. /package/cypress/{integration/Schema.spec.js → e2e/Schema.cy.js} +0 -0
  36. /package/cypress/support/{index.js → e2e.js} +0 -0
@@ -1,32 +1,2 @@
1
- const webpack = require('@cypress/webpack-preprocessor'),
2
- webpackOptions = {
3
- mode: 'development',
4
- devtool: 'cheap-module-source-map', // See https://survivejs.com/webpack/building/source-maps/
5
- module: {
6
- rules: [
7
- {
8
- test: /\.(js|jsx|mjs)$/,
9
- exclude: /node_modules\/(?!(@onehat)\/).*/,
10
- loader: 'babel-loader',
11
- options: {
12
- cacheDirectory: false,
13
- presets: [
14
- '@babel/preset-env'
15
- ],
16
- plugins: [
17
- '@babel/plugin-proposal-class-properties',
18
- '@babel/plugin-transform-runtime'
19
- ],
20
- sourceType: 'unambiguous',
21
- },
22
- }
23
- ]
24
- }
25
- };
26
-
27
- module.exports = (on, config) => {
28
- on('file:preprocessor', webpack({
29
- webpackOptions,
30
- watchOptions: {},
31
- }));
32
- };
1
+ module.exports = (on) => {
2
+ }
@@ -0,0 +1,42 @@
1
+ import { defineConfig } from "cypress";
2
+ import webpackPreprocessor from "@cypress/webpack-preprocessor";
3
+ import Webpack from "webpack";
4
+
5
+ export default defineConfig({
6
+ e2e: {
7
+ experimentalRunAllSpecs: true,
8
+ chromeWebSecurity: false,
9
+ setupNodeEvents(on) {
10
+ const options = webpackPreprocessor.defaultOptions;
11
+ if (!options.module) {
12
+ options.module = {
13
+ rules: [],
14
+ }
15
+ }
16
+ if (!options.module.rules) {
17
+ options.module.rules = [];
18
+ }
19
+ options.module.rules.push({
20
+ test: /\.(js|jsx|mjs)$/,
21
+ exclude: [/node_modules\/(?!(@onehat)\/).*/],
22
+ use: [{
23
+ loader: 'babel-loader',
24
+ options: {
25
+ cacheDirectory: false,
26
+ presets: [
27
+ '@babel/preset-env'
28
+ ],
29
+ plugins: [
30
+ '@babel/plugin-transform-class-properties',
31
+ '@babel/plugin-transform-runtime'
32
+ ],
33
+ sourceType: 'unambiguous',
34
+ },
35
+ }],
36
+
37
+ });
38
+
39
+ on('file:preprocessor', webpackPreprocessor(options));
40
+ },
41
+ }
42
+ });
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.17.0",
3
+ "version": "1.17.2",
4
4
  "description": "JS data modeling package with adapters for many storage mediums.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "test": "npx cypress open --config-file ../cypress.json",
8
+ "test": "npx cypress open",
9
+ "test2": "DEBUG=cypress:* npx cypress open",
9
10
  "docs": "jsdoc -c ./jsdoc.json -t ./node_modules/ink-docstrap/template -R README.md"
10
11
  },
11
12
  "repository": {
@@ -35,37 +36,36 @@
35
36
  },
36
37
  "homepage": "https://github.com/OneHatRepo/data#readme",
37
38
  "dependencies": {
38
- "@onehat/events": "^1.6.5",
39
+ "@onehat/events": "^1.6.6",
39
40
  "accounting-js": "^1.1.1",
40
- "axios": "^1.2.1",
41
- "chrono-node": "^2.4.2",
42
- "fast-xml-parser": "^4.0.12",
41
+ "axios": "^1.4.0",
42
+ "chrono-node": "^2.6.3",
43
+ "fast-xml-parser": "^4.2.2",
43
44
  "he": "^1.2.0",
44
- "js-base64": "^3.7.3",
45
+ "js-base64": "^3.7.5",
45
46
  "lodash": "^4.17.21",
46
47
  "moment": "^2.29.4",
47
48
  "natsort": "^2.0.3",
48
49
  "numeral": "^2.0.6",
49
- "qs": "^6.11.0",
50
+ "qs": "^6.11.2",
50
51
  "relative-time-parser": "^1.0.15",
51
52
  "uuid": "^9.0.0"
52
53
  },
53
54
  "devDependencies": {
54
- "@babel/core": "^7.20.5",
55
- "@babel/node": "^7.20.5",
56
- "@babel/plugin-proposal-class-properties": "^7.18.6",
55
+ "@babel/core": "^7.22.1",
56
+ "@babel/node": "^7.22.1",
57
+ "@babel/plugin-transform-class-properties": "^7.22.3",
57
58
  "@babel/plugin-transform-runtime": "^7.19.6",
58
- "@babel/preset-env": "^7.20.2",
59
- "@babel/register": "^7.18.9",
60
- "@babel/runtime": "^7.20.6",
61
- "@cypress/webpack-preprocessor": "^5.16.0",
62
- "babel-loader": "^8.0.2",
63
- "cypress": "5.2.0",
59
+ "@babel/preset-env": "^7.22.4",
60
+ "@babel/register": "^7.21.0",
61
+ "@babel/runtime": "^7.22.3",
62
+ "@cypress/webpack-preprocessor": "^5.17.1",
63
+ "babel-loader": "^9.1.2",
64
+ "cypress": "12.13.0",
64
65
  "ink-docstrap": "^1.3.2",
65
- "jsdoc": "^4.0.0",
66
- "webpack": "^5.76.0",
67
- "joi": "^17.7.0",
68
- "yup": "^0.32.11"
69
-
66
+ "joi": "^17.9.2",
67
+ "jsdoc": "^4.0.2",
68
+ "webpack": "^5.85.0",
69
+ "yup": "^1.2.0"
70
70
  }
71
71
  }
@@ -35,7 +35,7 @@ class Entity extends EventEmitter {
35
35
  * @param {boolean} originalIsMapped - Has data already been mapped according to schema?
36
36
  * @param {boolean} isDelayedSave - Should the repository skip autosave when immediately adding the record?
37
37
  */
38
- constructor(schema, rawData = {}, repository = null, originalIsMapped = false, isDelayedSave = false, isRemotePhantomMode = false) {
38
+ constructor(schema, rawData = {}, repository = null, originalIsMapped = false, isDelayedSave = false, isRemotePhantomMode = false, isRoot = false) {
39
39
  super(...arguments);
40
40
 
41
41
  if (!schema) {
@@ -104,9 +104,43 @@ class Entity extends EventEmitter {
104
104
  this.properties = [];
105
105
 
106
106
  /**
107
- * @member {boolean} isTreeNode - Whether this Entity is a TreeNode
107
+ * @member {boolean} isTree - Whether this Entity is a TreeNode
108
108
  */
109
- this.isTreeNode = false;
109
+ this.isTree = schema.model.isTree || false;
110
+
111
+ if (this.isTree && !schema.model.parentIdProperty) {
112
+ throw new Error('parentIdProperty cannot be empty for a TreeNode');
113
+ }
114
+ if (this.isTree && this.repository?.isClosureTable && !schema.model.depthProperty) {
115
+ throw new Error('depthProperty cannot be empty for a Closure Table TreeNode');
116
+ }
117
+ if (this.isTree && !schema.model.hasChildrenProperty) {
118
+ throw new Error('hasChildrenProperty cannot be empty for a TreeNode');
119
+ }
120
+
121
+ /**
122
+ * @member {boolean} isRoot - Whether this TreeNode is the root TreeNode
123
+ * For trees only
124
+ */
125
+ this.isRoot = false;
126
+
127
+ /**
128
+ * @member {TreeNode} parent - The parent TreeNode for this TreeNode
129
+ * For trees only
130
+ */
131
+ this.parent = null;
132
+
133
+ /**
134
+ * @member {array} children - Contains any children of this TreeNode
135
+ * For trees only
136
+ */
137
+ this.children = this._originalData.children && !_.isEmpty(this._originalData.children) ? this._originalData.children : [];
138
+
139
+ /**
140
+ * @member {boolean} isChildrenLoaded - Whether child TreeNodes have loaded for this TreeNode
141
+ * For trees only
142
+ */
143
+ this.isChildrenLoaded = this._originalData.isChildrenLoaded || false;
110
144
 
111
145
  /**
112
146
  * @member {boolean} isPersisted - Whether this object has been persisted in a storage medium
@@ -350,7 +384,7 @@ class Entity extends EventEmitter {
350
384
  * Assumes (and sets) isTempId === false.
351
385
  * @param {array} originalData - Raw data to load into entity.
352
386
  */
353
- loadOriginalData = (originalData) => {
387
+ loadOriginalData = (originalData, assembleTreeNodes = true) => {
354
388
  if (this.isDestroyed) {
355
389
  throw Error('this.loadOriginalData is no longer valid. Entity has been destroyed.');
356
390
  }
@@ -358,6 +392,10 @@ class Entity extends EventEmitter {
358
392
  this._originalData = originalData || {};
359
393
  this.reset();
360
394
  this.getIdProperty().isTempId = false;
395
+
396
+ if (this.isTree && this.repository && assembleTreeNodes) {
397
+ this.repository.assembleTreeNodes(); // rebuilds them all
398
+ }
361
399
  }
362
400
 
363
401
  /**
@@ -1432,6 +1470,292 @@ class Entity extends EventEmitter {
1432
1470
  }
1433
1471
 
1434
1472
 
1473
+
1474
+ // ______
1475
+ // /_ __/_______ ___ _____
1476
+ // / / / ___/ _ \/ _ \/ ___/
1477
+ // / / / / / __/ __(__ )
1478
+ // /_/ /_/ \___/\___/____/
1479
+
1480
+ /**
1481
+ * Gets the "parentId" Property object for this TreeNode.
1482
+ * This is the Property whose value represents the id for the parent TreeNode.
1483
+ * @return {Property} parentId Property
1484
+ */
1485
+ getParentIdProperty = () => {
1486
+ this.ensureTree();
1487
+ if (this.isDestroyed) {
1488
+ throw Error('this.getParentIdProperty is no longer valid. TreeNode has been destroyed.');
1489
+ }
1490
+
1491
+ const parentIdProperty = this.getSchema().model.parentIdProperty;
1492
+ return this.getProperty(parentIdProperty);
1493
+ }
1494
+
1495
+ /**
1496
+ * Gets the parentId for this TreeNode.
1497
+ * It does this by getting the parentId property's submitValue.
1498
+ * It doesn't look at some value created by client on the TreeNode.
1499
+ * @return {any} parentId - The parentId
1500
+ */
1501
+ getParentId = () => {
1502
+ this.ensureTree();
1503
+ if (this.isDestroyed) {
1504
+ throw Error('this.getParentId is no longer valid. TreeNode has been destroyed.');
1505
+ }
1506
+
1507
+ return this.geParentIdProperty().getSubmitValue();
1508
+ }
1509
+
1510
+ /**
1511
+ * Getter of parentId for this TreeNode.
1512
+ * @return {any} parentId - The parentId
1513
+ */
1514
+ get parentId() {
1515
+ return this.getParentId();
1516
+ }
1517
+
1518
+ /**
1519
+ * Getter of hasParent
1520
+ * Returns true if this is not a root node
1521
+ * @return {boolean} hasParent
1522
+ */
1523
+ get hasParent() {
1524
+ this.ensureTree();
1525
+
1526
+ return !this.isRoot;
1527
+ }
1528
+
1529
+ /**
1530
+ * Gets the "depth" Property object for this TreeNode.
1531
+ * This is the Property whose value represents the depth of the TreeNode.
1532
+ * @return {Property} parentId Property
1533
+ */
1534
+ getDepthProperty = () => {
1535
+ this.ensureTree();
1536
+ if (this.isDestroyed) {
1537
+ throw Error('this.getDepthProperty is no longer valid. TreeNode has been destroyed.');
1538
+ }
1539
+
1540
+ const depthProperty = this.getSchema().model.depthProperty;
1541
+ return this.getProperty(depthProperty);
1542
+ }
1543
+
1544
+ /**
1545
+ * Gets the depth for this TreeNode.
1546
+ * It does this by getting the depth property's submitValue.
1547
+ * @return {any} depth - The depth
1548
+ */
1549
+ getDepth = () => {
1550
+ this.ensureTree();
1551
+ if (this.isDestroyed) {
1552
+ throw Error('this.getDepth is no longer valid. TreeNode has been destroyed.');
1553
+ }
1554
+
1555
+ return this.getDepthProperty().getSubmitValue();
1556
+ }
1557
+
1558
+ /**
1559
+ * Getter of depth for this TreeNode.
1560
+ * @return {any} depth - The depth
1561
+ */
1562
+ get depth() {
1563
+ return this.getDepth();
1564
+ }
1565
+
1566
+ /**
1567
+ * Gets the "hasChildren" Property object for this TreeNode.
1568
+ * This is the Property whose value represents whether this TreeNode has any children.
1569
+ * @return {Property} parentId Property
1570
+ */
1571
+ getHasChildrenProperty = () => {
1572
+ this.ensureTree();
1573
+ if (this.isDestroyed) {
1574
+ throw Error('this.getHasChildrenProperty is no longer valid. TreeNode has been destroyed.');
1575
+ }
1576
+
1577
+ const hasChildrenProperty = this.getSchema().model.hasChildrenProperty;
1578
+ return this.getProperty(hasChildrenProperty);
1579
+ }
1580
+
1581
+ /**
1582
+ * Gets the hasChildren value for this TreeNode.
1583
+ * It does this by getting the hasChildren property's submitValue.
1584
+ * It doesn't look at some value created by client on the TreeNode.
1585
+ * @return {any} parentId - The parentId
1586
+ */
1587
+ getHasChildren = () => {
1588
+ this.ensureTree();
1589
+ if (this.isDestroyed) {
1590
+ throw Error('this.getHasChildren is no longer valid. TreeNode has been destroyed.');
1591
+ }
1592
+
1593
+ return this.getHasChildrenProperty().getSubmitValue();
1594
+ }
1595
+
1596
+ /**
1597
+ * Getter of hasChildren for this TreeNode.
1598
+ * @return {any} parentId - The parentId
1599
+ */
1600
+ get hasChildren() {
1601
+ return this.getHasChildren();
1602
+ }
1603
+
1604
+ /**
1605
+ * Getter of parent TreeNode for this TreeNode.
1606
+ * @return {TreeNode} parent - The parent TreeNode
1607
+ */
1608
+ getParent = () => {
1609
+ this.ensureTree();
1610
+ if (this.isDestroyed) {
1611
+ throw Error('this.getParent is no longer valid. TreeNode has been destroyed.');
1612
+ }
1613
+
1614
+ if (!this.hasParent) {
1615
+ return null;
1616
+ }
1617
+ return this.parent;
1618
+ }
1619
+
1620
+ /**
1621
+ * Getter of child TreeNodes for this TreeNode.
1622
+ * @return {array} children - The children
1623
+ */
1624
+ getChildren = async () => {
1625
+ this.ensureTree();
1626
+ if (this.isDestroyed) {
1627
+ throw Error('this.getChildren is no longer valid. TreeNode has been destroyed.');
1628
+ }
1629
+
1630
+ if (!this.isChildrenLoaded) {
1631
+ await this.loadChildren();
1632
+ }
1633
+ return this.children;
1634
+ }
1635
+
1636
+ /**
1637
+ * Loads the children of this TreeNode from repository.
1638
+ */
1639
+ loadChildren = async (depth) => {
1640
+ this.ensureTree();
1641
+ if (this.isDestroyed) {
1642
+ throw Error('this.loadChildren is no longer valid. TreeNode has been destroyed.');
1643
+ }
1644
+ if (!this.repository?.loadChildren) {
1645
+ throw Error('repository.loadChildren is not defined.');
1646
+ }
1647
+
1648
+ this.children = await this.repository.loadChildren(this, depth); // populates the children with a reference to this in child.parent
1649
+ this.isChildrenLoaded = true;
1650
+ }
1651
+
1652
+ /**
1653
+ * Alias for loadChildren
1654
+ */
1655
+ reloadChildren = () => { // alias
1656
+ return this.loadChildren();
1657
+ }
1658
+
1659
+ /**
1660
+ * Gets the previous sibling of this TreeNode from repository.
1661
+ * @return {TreeNode} sibling
1662
+ */
1663
+ getPrevousSibling = async () => {
1664
+ this.ensureTree();
1665
+ if (this.isDestroyed) {
1666
+ throw Error('this.getPrevousSibling is no longer valid. TreeNode has been destroyed.');
1667
+ }
1668
+
1669
+ const
1670
+ parent = this.getParent(),
1671
+ siblings = await parent.getChildren();
1672
+ let previous;
1673
+ _.each(siblings, (treeNode) => {
1674
+ if (treeNode === this) {
1675
+ return false;
1676
+ }
1677
+ previous = treeNode;
1678
+ })
1679
+ return previous;
1680
+ }
1681
+
1682
+ /**
1683
+ * Gets the next sibling of this TreeNode from repository.
1684
+ * @return {TreeNode} sibling
1685
+ */
1686
+ getNextSibling = async () => {
1687
+ this.ensureTree();
1688
+ if (this.isDestroyed) {
1689
+ throw Error('this.getNextSibling is no longer valid. TreeNode has been destroyed.');
1690
+ }
1691
+
1692
+ const
1693
+ parent = this.getParent(),
1694
+ siblings = await parent.getChildren();
1695
+ let returnNext = false,
1696
+ next = null;
1697
+ _.each(siblings, (treeNode) => {
1698
+ if (returnNext) {
1699
+ next = treeNode;
1700
+ return false;
1701
+ }
1702
+ if (treeNode === this) {
1703
+ returnNext = true;
1704
+ }
1705
+ })
1706
+ return next;
1707
+ }
1708
+
1709
+ /**
1710
+ * Gets the child of this TreeNode at index ix from repository.
1711
+ * @return {TreeNode} child
1712
+ */
1713
+ getChildAt = (ix) => {
1714
+ this.ensureTree();
1715
+ if (this.isDestroyed) {
1716
+ throw Error('this.getChildAt is no longer valid. TreeNode has been destroyed.');
1717
+ }
1718
+
1719
+ return this.children[ix];
1720
+ }
1721
+
1722
+ /**
1723
+ * Gets the first child of this TreeNode from repository.
1724
+ * @return {TreeNode} child
1725
+ */
1726
+ getFirstChild = () => {
1727
+ this.ensureTree();
1728
+ if (this.isDestroyed) {
1729
+ throw Error('this.getFirstChild is no longer valid. TreeNode has been destroyed.');
1730
+ }
1731
+
1732
+ return this.children[0];
1733
+ }
1734
+
1735
+ /**
1736
+ * Gets the last child of this TreeNode from repository.
1737
+ * @return {TreeNode} child
1738
+ */
1739
+ getLastChild = () => {
1740
+ this.ensureTree();
1741
+ if (this.isDestroyed) {
1742
+ throw Error('this.getLastChild is no longer valid. TreeNode has been destroyed.');
1743
+ }
1744
+
1745
+ return this.children.slice(-1)[0]
1746
+ }
1747
+
1748
+ /**
1749
+ * Helper to make sure this Repository is a tree
1750
+ * @private
1751
+ */
1752
+ ensureTree = async () => {
1753
+ if (!this.isTree) {
1754
+ this.throwError('This Repository is not a tree!');
1755
+ }
1756
+ }
1757
+
1758
+
1435
1759
  /**
1436
1760
  * Destroy this object.
1437
1761
  * - Removes all circular references to parent objects
@@ -430,6 +430,10 @@ class AjaxRepository extends Repository {
430
430
  this._relayEntityEvents(entity);
431
431
  return entity;
432
432
  });
433
+
434
+ if (this.isTree) {
435
+ this.assembleTreeNodes();
436
+ }
433
437
 
434
438
  // Set the total records that pass filter
435
439
  this.total = total;
@@ -492,7 +496,7 @@ class AjaxRepository extends Repository {
492
496
  }
493
497
 
494
498
  const updatedData = root[0];
495
- entity.loadOriginalData(updatedData, true);
499
+ entity.loadOriginalData(updatedData);
496
500
  entity.emit('reload', entity);
497
501
 
498
502
  this.markLoaded();
@@ -638,11 +642,14 @@ class AjaxRepository extends Repository {
638
642
  // Reload each entity with new data
639
643
  // TODO: Check this
640
644
  _.each(entities, (entity, ix) => {
641
- entity.loadOriginalData(root[ix]);
645
+ entity.loadOriginalData(root[ix], false); // false to not assembleTreeNodes
642
646
  if (entity.isRemotePhantomMode) {
643
647
  entity.isRemotePhantom = true;
644
648
  }
645
649
  });
650
+ if (this.isTree) {
651
+ this.assembleTreeNodes();
652
+ }
646
653
  });
647
654
  }
648
655
 
@@ -748,11 +755,14 @@ class AjaxRepository extends Repository {
748
755
  // Reload each entity with new data
749
756
  // TODO: Check this
750
757
  _.each(entities, (entity, ix) => {
751
- entity.loadOriginalData(root[ix]);
758
+ entity.loadOriginalData(root[ix], false); // false to not assembleTreeNodes
752
759
  if (entity.isRemotePhantomMode && entity.isRemotePhantom) {
753
760
  entity.isRemotePhantom = false;
754
761
  }
755
762
  });
763
+ if (this.isTree) {
764
+ this.assembleTreeNodes();
765
+ }
756
766
  });
757
767
  }
758
768
 
@@ -404,6 +404,86 @@ class OneBuildRepository extends AjaxRepository {
404
404
  });
405
405
  }
406
406
 
407
+
408
+ // ______
409
+ // /_ __/_______ ___ _____
410
+ // / / / ___/ _ \/ _ \/ ___/
411
+ // / / / / / __/ __(__ )
412
+ // /_/ /_/ \___/\___/____/
413
+
414
+ /**
415
+ * Gets the root nodes of this tree.
416
+ */
417
+ getRootNodes = async (getChildren = false, depth) => {
418
+ this.ensureTree();
419
+ if (this.isDestroyed) {
420
+ this.throwError('this.setRootNode is no longer valid. Repository has been destroyed.');
421
+ return;
422
+ }
423
+
424
+ // Clear all entities, if any exist
425
+ _.each(this.entities, (entity) => {
426
+ entity.destroy();
427
+ });
428
+ this.entities = [];
429
+
430
+ // TODO: Load root nodes (and possibly their children)
431
+
432
+
433
+ }
434
+
435
+ /**
436
+ * Loads the children of the supplied treeNode
437
+ */
438
+ loadChildren = async (treeNode, depth = 1) => {
439
+ this.ensureTree();
440
+ if (this.isDestroyed) {
441
+ this.throwError('this.setRootNode is no longer valid. Repository has been destroyed.');
442
+ return;
443
+ }
444
+
445
+
446
+ // If children already exist, remove them from the repository
447
+ // This way, we can reload just a portion of the tree
448
+ if (!_.isEmpty(treeNode.children)) {
449
+ const children = treeNode.children;
450
+ treeNode.children = [];
451
+
452
+ _.each(children, (child) => {
453
+ this.removeNode(child);
454
+ });
455
+ }
456
+
457
+
458
+ // TODO: load children here
459
+
460
+
461
+
462
+ }
463
+
464
+ /**
465
+ * Alias for loadChildren
466
+ */
467
+ reloadChildren = (treeNode, depth) => {
468
+ return this.loadChildren(treeNode, depth);
469
+ }
470
+
471
+ /**
472
+ * Moves the supplied treeNode to a new position on the tree
473
+ */
474
+ moveTreeNode = async (treeNode, newParentId) => {
475
+ this.ensureTree();
476
+ if (this.isDestroyed) {
477
+ this.throwError('this.moveTreeNode is no longer valid. Repository has been destroyed.');
478
+ return;
479
+ }
480
+
481
+ // TODO: move node here
482
+
483
+
484
+
485
+ }
486
+
407
487
  }
408
488
 
409
489