dataclasses/predicates.js

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