@ironcode/vas-lib 1.1.0 → 1.2.0

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.
@@ -1675,8 +1675,7 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1675
1675
  .filter(prop => !nativeProps.includes(prop));
1676
1676
  }
1677
1677
  /**
1678
- * Returns a list of Job static properties i.e. those that are declared by
1679
- * the type
1678
+ * Returns the list of properties of the Job type
1680
1679
  */
1681
1680
  get staticProperties() {
1682
1681
  return Object.getOwnPropertyNames(VasJobModel.empty());
@@ -1696,6 +1695,27 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1696
1695
  .forEach((key) => model.$this[key] = dto[key]);
1697
1696
  return model;
1698
1697
  }
1698
+ /**
1699
+ * This method will instantiate a new JobModel. The difference with this
1700
+ * method of instantiation is that we are coming from a relation frame i.e.
1701
+ * the job has a list of {@link VasFieldDto} instead of a Job document.
1702
+ *
1703
+ */
1704
+ static fromRelational(dto, form) {
1705
+ const model = new VasJobModel(dto.id || '', dto.created || '', dto.serverCreated || '', dto.createdBy || '', dto.modified || '', dto.serverModified || '', dto.modifiedBy || '', dto.createdByName || '', dto.modifiedByName || '', dto.account || '', dto.accessGroup || '', dto.reference || '', dto.jobDate || '', dto.jobStatus || '', dto.jobType || '', dto.assigneeId || '', dto.formId || '', dto.timeZoneOffset || moment$1().utcOffset(), dto.pendingFields || 0, dto.childModified || '', dto.version || 0, dto.fields || [], dto.files || [], dto.createdByDisplayName || '', dto.modifiedByDisplayName || '', dto.geoLocation || getEmptyGeoLocation());
1706
+ form.groups
1707
+ .forEach(group => {
1708
+ group.controls
1709
+ .forEach(control => {
1710
+ const field = dto.fields?.find(f => f.control === control.id);
1711
+ if (!field) {
1712
+ return;
1713
+ }
1714
+ model.getGroup(group.name)[control.name] = field.value;
1715
+ });
1716
+ });
1717
+ return model;
1718
+ }
1699
1719
  /**
1700
1720
  * @param {VasFormModel} formModel
1701
1721
  * @return {Record<string, VasFieldDtoValue>}
@@ -1776,18 +1796,63 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1776
1796
  };
1777
1797
  }
1778
1798
  /**
1779
- * This method will hydrate the `fields` property of the model. The reason for
1780
- * this is that we have different ways to store the field data. One way, is
1781
- * we store them as dynamic properties of the job. For example job.foo.bar,
1782
- * where `foo` is the name of a Group, and `bar` is the name of a control.
1783
- * Thus, when we create a job using a form in the client, the job object will
1784
- * have its static properties (id, account, reference etc), and also a number
1785
- * of dynamic properties determined by the Groups and Controls. This kind of
1786
- * object is nice to work with in certain circumstances. However, the api
1787
- * works differently. In the API a Job is a record, and references a number of
1788
- * Field records. Each Field stores the value. Comparing these two models we
1789
- * have:
1790
- * A) job with dynamic properties, e.g.
1799
+ * This method will return the dynamic property from the JobModel that
1800
+ * represent a group (from a form).
1801
+ *
1802
+ * @param name the name of the group
1803
+ * @param init if true (default) and group is not found, initialise an empty
1804
+ * group, otherwise throw an error
1805
+ */
1806
+ getGroup(name, init = true) {
1807
+ let prop;
1808
+ if (this.staticProperties.includes(name)) {
1809
+ throw Error(`invalid group name ${name}, not a dynamic property`);
1810
+ }
1811
+ else if (this.$this[name] === undefined) {
1812
+ if (init) {
1813
+ prop = this.$this[name] = {};
1814
+ }
1815
+ else {
1816
+ throw Error(`invalid group name ${name}, not found`);
1817
+ }
1818
+ }
1819
+ else {
1820
+ prop = this.$this[name];
1821
+ if (typeof prop !== 'object') {
1822
+ throw Error(`invalid group name ${name}, not an object`);
1823
+ }
1824
+ }
1825
+ return prop;
1826
+ }
1827
+ /**
1828
+ * @param path path segments
1829
+ */
1830
+ getValueByPath(path = []) {
1831
+ return getValueByPath(path, this.$this);
1832
+ }
1833
+ /**
1834
+ * In order to understand why we need this method it is important to
1835
+ * understand that within the system, Jobs can be represented in one of two
1836
+ * ways, document and relational.
1837
+ *
1838
+ * The important distinction is how values submitted by a form are stored.
1839
+ *
1840
+ * Jobs stored as documents (JSON objects) will store user values, as dynamic
1841
+ * properties of the document.
1842
+ *
1843
+ * Whereas, Jobs stored as relational, will store user values in an array of
1844
+ * {@link VasFieldDto} objects.
1845
+ *
1846
+ * Depending on where we are in the system, either one of these approaches can
1847
+ * be more useful than the other.
1848
+ *
1849
+ * This method, assumes that the JobModel has been instantiated from a
1850
+ * document representation, and serves to hydrate the fields array. In order
1851
+ * to achieve this, knowledge of the {@link VasFormDto} that created the job
1852
+ * is required.
1853
+ *
1854
+ *
1855
+ * Job in document representation
1791
1856
  * {
1792
1857
  * id: <guid>,
1793
1858
  * reference: "something"
@@ -1797,7 +1862,7 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1797
1862
  * }
1798
1863
  * }
1799
1864
  *
1800
- * B) job with fields
1865
+ * Job in relational representation
1801
1866
  * {
1802
1867
  * id: <guid>,
1803
1868
  * reference: "something"
@@ -1812,46 +1877,11 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1812
1877
  * ]
1813
1878
  * }
1814
1879
  *
1815
- * So, what this method does is given a JobModel in the form of A, read all
1816
- * of those dynamic properties and set them into `fields`. Doing this requires
1817
- * knowledge of the Form that was used to create the job. Moreover, since the
1818
- * dynamic properties do not contain the ids of the fields, we also allow to
1819
- * pass in a `controlFieldIdMap`. This map stores the mapping between Control
1820
- * and the Field that was created in the Job to store the value for that
1821
- * Control. This is useful, if for example you want to compare a Job in form A
1822
- * with a Job in form B, for example if you want to update the Job on the API
1823
- * with a Job that was saved by a client in form A.
1824
- *
1825
- * E.g.
1826
- * Client -> API: client requests form
1827
- * User -> Client: user fills in the form and submits
1828
- * Client -> Firestore: client saves the Job in form A i.e. dynamic props
1829
- * Firestore -> Function: A function is triggered to sync the job to the API
1830
- * Function -> API: Function checks if job already exists, it receives 404
1831
- * Function -> Function: The function calls `hydrateFields(...)`
1832
- * Function -> API: The function POST the Job to /jobs
1833
- * Function -> API: The function POST each field to /fields
1834
- *
1835
- * Similarly, if the user updates the job
1836
- * Client -> API: client requests form
1837
- * User -> Client: user fills in the form and submits an update
1838
- * Client -> Firestore: client saves the Job in form A i.e. dynamic props
1839
- * Firestore -> Function: A function is triggered to sync the job to the API
1840
- * Function -> API: Function checks if job already exists, it receives 200
1841
- * Function -> Function: The function calls `hydrateFields(...)` passing in
1842
- * the map is made by iterating over the fields it
1843
- * received from the API and storing the mappings
1844
- * between controlId and fieldId for each field
1845
- * Function -> API: The function PATCH the Job to /jobs
1846
- * Function -> API: The function POST/PATCH each field to /fields
1847
- * treated as new
1848
1880
  *
1849
- * @param {VasFormModel} formModel the VasFormModel that was used to create
1850
- * the job
1851
- * @param {Map<string, string>} controlFieldIdMap a mapping of control to
1852
- * field ids. This is used to determine whether a new id for the field should
1853
- * be generated, or to reuse an existing one from the map.
1854
- * @param {Array<string>} controlNames if a value is provided, it will be used
1881
+ * @param formModel the VasFormModel that was used to create the job
1882
+ * @param controlFieldIdMap This is used to determine the id each field.
1883
+ * Either one will be found in the map, or a new one is generated.
1884
+ * @param controlNames if a value is provided, it will be used
1855
1885
  * to filter the fields that are returned.
1856
1886
  * @return {Array<VasFieldDto>}
1857
1887
  */
@@ -1883,13 +1913,6 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1883
1913
  });
1884
1914
  this.fields = fields;
1885
1915
  }
1886
- /**
1887
- * @param {string[]} path path segments
1888
- * @return {void}
1889
- */
1890
- getValueByPath(path = []) {
1891
- return getValueByPath(path, this.$this);
1892
- }
1893
1916
  /**
1894
1917
  * A very non sophisticated way to set values in the job via paths
1895
1918
  *
@@ -1904,8 +1927,8 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1904
1927
  * }
1905
1928
  * }
1906
1929
  *
1907
- * @param {any} value the value to set
1908
- * @param {string[]} path path segments
1930
+ * @param value the value to set
1931
+ * @param path path segments
1909
1932
  */
1910
1933
  setValueByPath(value, path = []) {
1911
1934
  switch (path.length) {
@@ -1932,9 +1955,8 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1932
1955
  }
1933
1956
  }
1934
1957
  /**
1935
- * @param {boolean} staticOnly if true, will only output values for the static
1958
+ * @param staticOnly if true, will only output values for the static
1936
1959
  * properties in the dto
1937
- * @return {VasJobDto}
1938
1960
  */
1939
1961
  toDto(staticOnly = false) {
1940
1962
  if (staticOnly) {
@@ -1969,7 +1991,7 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1969
1991
  }
1970
1992
  const dto = {};
1971
1993
  [...this.staticProperties, ...this.dynamicProperties]
1972
- .forEach(prop => (dto)[prop] = this.$this[prop]);
1994
+ .forEach(prop => dto[prop] = this.$this[prop]);
1973
1995
  return dto;
1974
1996
  }
1975
1997
  /**
@@ -1996,7 +2018,7 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
1996
2018
  *
1997
2019
  * @param {string} value a string with the syntax
1998
2020
  * @param {ParseSyntaxOptions} options
1999
- * @return {string} the results of parsing the syntax on this job
2021
+ * @return the results of parsing the syntax on this job
2000
2022
  */
2001
2023
  parseSyntax(value, options = {
2002
2024
  timeZoneOffset: 0
@@ -2030,7 +2052,7 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
2030
2052
  result = (this.getValueByPath(path) || '').toString();
2031
2053
  }
2032
2054
  else if (objectKey === 'fields') {
2033
- result = (getValueByPath(path, this.getFields2()) || '').toString();
2055
+ result = (getValueByPath(['fields.' + path.shift(), ...path], this.getFields2()) || '').toString();
2034
2056
  }
2035
2057
  else if (objectKey.length) {
2036
2058
  if (options.objects) {