model/Model.js

  1. import { checkType, hasKeys } from '../lib/check'
  2. import { pickOr } from '../lib/utils'
  3. import ModelBase, { DIRTY_PROPERTY_LIST } from './ModelBase'
  4. import createPropertyDefinitionsForAttributes from './helpers/attibutes'
  5. const pickAttributeValues = pickOr('attributeValues', [])
  6. // TODO: Perhaps we can generate model classes dynamically based on the schemas and inherit from this.
  7. /**
  8. * @extends ModelBase
  9. *
  10. * @description
  11. * A Model represents an object from the DHIS2 Api. A model is created based of a ModelDefinition. The ModelDefinition
  12. * has the properties that the model should have.
  13. *
  14. * @memberof module:model
  15. */
  16. class Model extends ModelBase {
  17. /**
  18. * @constructor
  19. *
  20. * @param {ModelDefinition} modelDefinition The model definition that corresponds with the model.
  21. * This is essential defining what type the model is representing.
  22. *
  23. * @description
  24. * Will create a new model instanced based on the model definition. When creating a new instance the model
  25. * definition needs to have both the modelValidations and modelProperties.
  26. *
  27. * The model properties will depend on the ModelDefinition. A model definition is based on a DHIS2 Schema.
  28. */
  29. constructor(modelDefinition) {
  30. super()
  31. checkType(modelDefinition, 'object', 'modelDefinition')
  32. checkType(modelDefinition.modelProperties, 'object', 'modelProperties')
  33. /**
  34. * @property {ModelDefinition} modelDefinition Stores reference to the modelDefinition that was used when
  35. * creating the model. This property is not enumerable or writable and will therefore not show up when looping
  36. * over the object properties.
  37. */
  38. Object.defineProperty(this, 'modelDefinition', {
  39. value: modelDefinition,
  40. })
  41. /**
  42. * @property {Boolean} dirty Represents the state of the model. When the model is concidered `dirty`
  43. * there are pending changes.
  44. * This property is not enumerable or writable and will therefore not show up when looping
  45. * over the object properties.
  46. */
  47. Object.defineProperty(this, 'dirty', { writable: true, value: false })
  48. /**
  49. * @private
  50. * @property {Object} dataValues Values object used to store the actual model values. Normally access to the
  51. * Model data will be done through accessor properties that are generated from the modelDefinition.
  52. *
  53. * @note {warning} This should not be accessed directly.
  54. */
  55. Object.defineProperty(this, 'dataValues', {
  56. configurable: true,
  57. writable: true,
  58. value: {},
  59. })
  60. /**
  61. * Attach the modelDefinition modelProperties (the properties from the schema) onto the Model.
  62. *
  63. * For a data element model the modelProperties would be the following
  64. * https://play.dhis2.org/demo/api/schemas/dataElement.json?fields=properties
  65. */
  66. Object.defineProperties(this, modelDefinition.modelProperties)
  67. const attributes = {}
  68. const { attributeProperties } = modelDefinition
  69. if (hasKeys(attributeProperties)) {
  70. /**
  71. * @property {Object} attributes The attributes objects contains references to custom attributes defined
  72. * on the metadata object.
  73. *
  74. * @description
  75. * These properties are generated based of the attributes that are created for the the specific schema.
  76. * As these properties are not defined on the schemas they're put on a separate attributes object.
  77. * When there are no attributes defined for the object type, the attributes property will not be attached
  78. * to the model.
  79. *
  80. * @see https://docs.dhis2.org/2.27/en/user/html/dhis2_user_manual_en_full.html#manage_attribute
  81. */
  82. Object.defineProperty(this, 'attributes', { value: attributes })
  83. const getAttributeValues = () => pickAttributeValues(this)
  84. const setAttributeValues = attributeValues =>
  85. (this.attributeValues = attributeValues)
  86. const setDirty = () => (this.dirty = true)
  87. const attributeDefinitions = createPropertyDefinitionsForAttributes(
  88. attributeProperties,
  89. getAttributeValues,
  90. setAttributeValues,
  91. setDirty
  92. )
  93. Object.defineProperties(attributes, attributeDefinitions)
  94. }
  95. this[DIRTY_PROPERTY_LIST] = new Set([])
  96. }
  97. /**
  98. * @static
  99. *
  100. * @param {ModelDefinition} modelDefinition ModelDefinition from which the model should be created
  101. * @returns {Model} Returns an instance of the model.
  102. *
  103. * @description The static method is a factory method to create Model objects. It calls `new Model()` with the passed `ModelDefinition`.
  104. *
  105. * ```js
  106. * let myModel = Model.create(modelDefinition);
  107. * ```
  108. */
  109. static create(modelDefinition) {
  110. return new Model(modelDefinition)
  111. }
  112. }
  113. export default Model