dataclasses/predicates.js

  1. const merge = require('ramda/src/merge');
  2. const mergeAll = require('ramda/src/mergeAll');
  3. const $_type = require('./$_type');
  4. module.exports = ({ invariants, errors }) => {
  5. const { Target, Operator, LogicalType } = require('./columns');
  6. /**
  7. * Abstract Predicate type, a Predicate is the union type of CompoundPredicate | ComparisonPredicate
  8. * @typedef {object} Predicate
  9. * @memberof dataclasses
  10. */
  11. /**
  12. * Abstract Predicate type, a Predicate is the union type of CompoundPredicate | ComparisonPredicate
  13. * @param {Function} type - Predicate subtype function constructor
  14. * @return {dataclasses.Predicate} predicate
  15. * @memberof dataclasses
  16. */
  17. function Predicate(type) {
  18. return invariants
  19. .PredicateTypeMustBeValid(type.$name, Predicate.Types)
  20. .then(() =>
  21. merge($_type(type.$name), {
  22. /**
  23. * $canBeRemoved specify if the predicate can be removed or not from the Predicates tree
  24. * @type {Boolean}
  25. * @memberof Predicate
  26. */
  27. $canBeRemoved: true,
  28. })
  29. );
  30. }
  31. /**
  32. * [description]
  33. * @param {dataclasses.Predicate} predicate predicate
  34. * @return {Object} serializable object
  35. */
  36. Predicate.toJSON = function(predicate) {
  37. if (ComparisonPredicate.is(predicate))
  38. return ComparisonPredicate.toJSON(predicate);
  39. return CompoundPredicate.toJSON(predicate);
  40. };
  41. /**
  42. * @param {object} json json
  43. * @param {object} internalAPI internalAPI
  44. * @return {Promise<Predicate, errors<*>>} Promise
  45. */
  46. Predicate.fromJSON = function(json, internalAPI) {
  47. if (ComparisonPredicate.isFromJSON(json))
  48. return ComparisonPredicate.fromJSON(json, internalAPI);
  49. if (CompoundPredicate.isFromJSON(json))
  50. return CompoundPredicate.fromJSON(json, internalAPI);
  51. return Promise.reject(new errors.UnknownJSONData());
  52. };
  53. Predicate.Types = {
  54. ComparisonPredicate: 'ComparisonPredicate',
  55. CompoundPredicate: 'CompoundPredicate',
  56. };
  57. /**
  58. * Abstract Predicate type, a Predicate is the union type of CompoundPredicate | ComparisonPredicate
  59. * @typedef {object} ComparisonPredicate
  60. * @param {string} target - unique id for this target
  61. * @param {string} operator - label that will be displayed for this target
  62. * @param {string} argument - the type_id name this target has
  63. * @memberof dataclasses
  64. */
  65. /**
  66. * A specialized predicate that you use to compare expressions.
  67. * @param {dataclasses.Target} target target
  68. * @param {dataclasses.Operator} operator operator
  69. * @param {*} argument argument
  70. * @return {Promise<dataclasses.ComparisonPredicate>} yield a ComparisonPredicate or a rejected promise
  71. * @memberof dataclasses
  72. */
  73. function ComparisonPredicate(target, operator, argument = null) {
  74. return Predicate(ComparisonPredicate).then(predicate =>
  75. merge(predicate, {
  76. target,
  77. operator,
  78. argument,
  79. })
  80. );
  81. }
  82. // by pass var. mangling from minify
  83. ComparisonPredicate.$name = Predicate.Types.ComparisonPredicate;
  84. /**
  85. * @param {ComparisonPredicate} predicate predicate
  86. * @return {Object} JSON serializable object
  87. */
  88. ComparisonPredicate.toJSON = function(predicate) {
  89. return mergeAll([
  90. Target.toJSON(predicate.target),
  91. Operator.toJSON(predicate.operator),
  92. {
  93. argument: predicate.argument,
  94. },
  95. ]);
  96. };
  97. /**
  98. * @param {object} json json
  99. * @param {object} internalAPI internalAPI
  100. * @return {Promise<Predicate, errors<*>>} Promise
  101. */
  102. ComparisonPredicate.fromJSON = function(json, internalAPI) {
  103. return Promise.all([
  104. internalAPI.getTargetById(json.target_id),
  105. internalAPI.getOperatorById(json.operator_id),
  106. ]).then(([target, operator]) =>
  107. ComparisonPredicate(target, operator, json.argument)
  108. );
  109. };
  110. /**
  111. * Yield true if `predicate` is a ComparisonPredicate
  112. * @param {dataclasses.Predicate} predicate {@link dataclasses.Predicate}
  113. * @return {Boolean} true if `predicate` is a ComparisonPredicate
  114. * @memberof dataclasses
  115. */
  116. ComparisonPredicate.is = predicate => {
  117. return (
  118. predicate && predicate.$_type === Predicate.Types.ComparisonPredicate
  119. );
  120. };
  121. /**
  122. * Yield true if `json` seems to be a ComparisonPredicate
  123. * @param {object} json json
  124. * @private
  125. * @return {Boolean} true if json seems to be a ComparisonPredicate
  126. * @memberof dataclasses
  127. */
  128. ComparisonPredicate.isFromJSON = json => json && json.target_id;
  129. /**
  130. * A specialized predicate that evaluates logical combinations of other predicates.
  131. * @param {dataclasses.LogicalType} logic The predicate logic
  132. * @param {Array<dataclasses.Predicate>} predicates predicates predicates
  133. * @return {Promise<dataclasses.CompoundPredicate>} yield a {@link dataclasses.CompoundPredicate} or a {@link errors.CompoundPredicateMustHaveAtLeastOneSubPredicate} rejected promise
  134. * @memberof dataclasses
  135. */
  136. function CompoundPredicate(logic, predicates) {
  137. return invariants
  138. .CompoundPredicateMustHaveAtLeastOneSubPredicate(
  139. predicates,
  140. CompoundPredicate
  141. )
  142. .then(() => Predicate(CompoundPredicate))
  143. .then(predicate =>
  144. merge(predicate, {
  145. logic,
  146. predicates,
  147. })
  148. );
  149. }
  150. // by pass var. mangling from minify
  151. CompoundPredicate.$name = Predicate.Types.CompoundPredicate;
  152. /**
  153. * @param {CompoundPredicate} predicate predicate
  154. * @return {Object} JSON serializable object
  155. */
  156. CompoundPredicate.toJSON = function(predicate) {
  157. return mergeAll([
  158. LogicalType.toJSON(predicate.logic),
  159. { predicates: predicate.predicates.map(Predicate.toJSON) },
  160. ]);
  161. };
  162. /**
  163. * @param {CompoundPredicate} predicate predicate
  164. * @param {object} internalAPI ui-predicate-core internal api object
  165. * @return {Promise<CompoundPredicate, errors<*>>} Promise
  166. */
  167. CompoundPredicate.fromJSON = function(predicate, internalAPI) {
  168. return invariants
  169. .CompoundPredicateMustHaveAtLeastOneSubPredicate(
  170. predicate.predicates,
  171. CompoundPredicate
  172. )
  173. .then(() => internalAPI.getLogicalTypeById(predicate.logicalType_id))
  174. .then(logicalType =>
  175. Promise.all(
  176. predicate.predicates.map(predicate =>
  177. Predicate.fromJSON(predicate, internalAPI)
  178. )
  179. ).then(predicates => CompoundPredicate(logicalType, predicates))
  180. );
  181. };
  182. /**
  183. * Reduce through the predicates tree
  184. * @param {dataclasses.CompoundPredicate} compoundPredicate starter node
  185. * @param {function} f accumulation function, f(acc, predicate, parents)
  186. * @param {T} acc accumulator
  187. * @param {Array} [parents=[]] path to the node, array of parents
  188. * @return {T} yield the accumulator
  189. * @memberof dataclasses
  190. */
  191. CompoundPredicate.reduce = function(compoundPredicate, f, acc, parents = []) {
  192. const accumulator = f(acc, compoundPredicate, parents);
  193. return compoundPredicate.predicates.reduce((_acc, predicate, i) => {
  194. const _parents = parents.concat([compoundPredicate, [predicate, i]]);
  195. return CompoundPredicate.is(predicate)
  196. ? CompoundPredicate.reduce(predicate, f, _acc, _parents)
  197. : f(_acc, predicate, _parents);
  198. }, accumulator);
  199. };
  200. /**
  201. * Walk through the predicates tree
  202. * @param {dataclasses.CompoundPredicate} compoundPredicate starter node
  203. * @param {Function} f(predicate) iterator function
  204. * @return {undefined}
  205. * @memberof dataclasses
  206. */
  207. CompoundPredicate.forEach = (compoundPredicate, f) => {
  208. CompoundPredicate.reduce(
  209. compoundPredicate,
  210. (_, predicate) => {
  211. f(predicate);
  212. },
  213. null
  214. );
  215. };
  216. /**
  217. * Yield true if `predicate` is a CompoundPredicate
  218. * @param {dataclasses.Predicate} predicate predicate
  219. * @return {Boolean} true if `predicate` is a CompoundPredicate
  220. * @memberof dataclasses
  221. */
  222. CompoundPredicate.is = predicate =>
  223. predicate && predicate.$_type === Predicate.Types.CompoundPredicate;
  224. /**
  225. * Yield true if `json` seems to be a CompoundPredicate
  226. * @param {object} json json
  227. * @private
  228. * @return {Boolean} true if json seems to be a CompoundPredicate json
  229. * @memberof dataclasses
  230. */
  231. CompoundPredicate.isFromJSON = json => json && json.logicalType_id;
  232. return {
  233. Predicate,
  234. ComparisonPredicate,
  235. CompoundPredicate,
  236. };
  237. };